/*
 * handle_player.cpp
 *
 *  Created on: Aug 30, 2013
 *      Author: killerg
 */

#include "../../server_deps.h"
#include "../../server.h"
#include "../../client_session.h"
#include "../../gm_cmd_handler.h"
#include "../../auth_mgr.h"
#include "../../server_cfg.h"
#include "../../server_console.h"
#include "../../bill_session.h"
#include "../../client_stat.h"

#include "../player_data.h"
#include "../player_mgr.h"
#include "../combat.h"
#include "../arena.h"
#include "../rank_mgr.h"
#include "../game_cfg.h"
#include "../mail_mgr.h"
#include "../chat_mgr.h"
#include "../world_boss.h"
#include "../pointrank.h"
#include "../guild.h"

#include "share/net_s.h"
#include "share/connection_mgr.h"
#include "share/msg.h"
#include "share/sl_msg.pb.h"
#include "share/word_filter.h"

#include "share/profiler.h"

void handle_cecho(client_session_t* sess, cs::CMsg* msg) {
	CREATE_CS_SMSG(SEcho, echo);
	auto secho = echo->mutable_echo();

	string str;
	for (int i = 0; i < 10; ++i) {
		str += msg->echo().echo().str();
	}
	secho->set_str(str);
	secho->set_stamp(msg->echo().echo().stamp());
	sess->send_msg(___msg);
}

void handle_cload(client_session_t* sess, cs::CMsg* msg) {
//    UTIL_LOG(T, "client load");

	if (g_svr.status != co::ESS_Running) {
		sess->_trans->disconnect();
		return;
	}

	if (sess->stage != ESCreated && sess->stage != ESConnedted) {
		sess->_trans->disconnect();
		return;
	}

	if (NULL != sess->player) {
		sess->send_notify("player already loaded");
		return;
	}

	const auto &load = msg->load();

	if (sess->stage == ESConnedted) {
		auto itr = g_authmgr->auth_data.find(load.playerid());
		if (itr == g_authmgr->auth_data.end()) {
//            UTIL_LOG(T, "client timeout");

			CREATE_CS_SMSG(SLoad, load);
			load->set_timeout(true);
			sess->send_msg(___msg);
			sess->_trans->disconnect();

			return;
		}

		if (itr->second.key != load.key()) {
//            UTIL_LOG(T, "client auth failed");

			CREATE_CS_SMSG(SLoad, load);
			load->set_timeout(true);
			sess->send_msg(___msg);
			sess->_trans->disconnect();

			return;
		}
		sess->playerid = load.playerid();
		sess->stat.playerid = load.playerid();
		sess->account = itr->second.account;
		sess->plat = itr->second.channel;
		sess->stat.account = itr->second.account;
		sess->stat.machine_code = itr->second.mc;
		sess->active = itr->second.active;
	}

	sess->stage = ESLoading;
	sess->stat.load_start = g_clock->tt_now;
	int64_t sessid = sess->_sessid;
	g_playermgr->player_load(sess->playerid, [sessid](sp_player_t player) {
		auto sess = g_sessmgr->base::connmgr.map_id.get(sessid);
		if (NULL == sess)
		return;

		CREATE_CS_SMSG(SLoad, load);
		if (player) {
			if (player->sess != NULL && player->sess->_sessid != sessid) {
				load->set_already_online(true);
				sess->_trans->disconnect();
				player->sess->_trans->disconnect();
			} else {
				sess->player = player;
				sess->player->fill_oplayer(load->mutable_player());
				sess->player->on_logon(sess);
				sess->stage = ESLoaded;
				sess->stat.load_end = g_clock->tt_now;
				sess->stat.name = player->att(Plss_Name);
				load->set_now(g_clock->tt_now);
			}
		} else {
			sess->stage = ESNeedCreate;
		}
		sess->send_msg(___msg);
	});
}

void handle_ccreate(client_session_t* sess, cs::CMsg* msg) {
//    UTIL_LOG(T, "client create");
	if (sess->stage != ESNeedCreate) {
		sess->_trans->disconnect();
		return;
	}

	if (NULL != sess->player)
		return;

	if (g_playermgr->name_table.count(msg->create().name()) > 0
			|| !g_wordfilter->checkName(msg->create().name().c_str(),
					EFM_CreateRole, 1, 8)) {
		CREATE_CS_SMSG(SCreate, create);
		create->set_ok(false);
		sess->send_msg(___msg);
		return;
	}

	sess->stage = ESCreating;
	sess->stat.create_start = g_clock->tt_now;

	int64_t sessid = sess->_sessid;
	string playername = msg->create().name();
	g_playermgr->player_create(sess->playerid, playername, msg->create().sex(),
			sess->account,
			[sessid, playername](db_error_t err) {
				auto sess = g_sessmgr->base::connmgr.map_id.get(sessid);
				if (NULL == sess)
					return;

				CREATE_CS_SMSG(SCreate, create);
				create->set_ok(DBE_Success == err);
				sess->send_msg(___msg);

				if (err == DBE_Success) {
					sess->stage = ESCreated;
					g_bill->on_create_player(sess->account, playername, sess->playerid % co::MaxAccountNum);
					sess->stat.create_end = g_clock->tt_now;
				} else
				sess->_trans->disconnect();
			});
}

void handle_cgm(client_session_t* sess, cs::CMsg* msg) {
	UTIL_LOG(T, "gm");
	if (!g_cfg->gm_cmd) {
		if (g_cfg->GmAccount != sess->account) {
			sess->_trans->disconnect();
			return;
		}
	}

	sess->send_notify(
	g_gm_cmd_handler->proc_gmcmd(msg->gm().cmd().c_str(), sess).c_str());
}

void handle_csellitem(client_session_t* sess, cs::CMsg* msg) {
	UTIL_LOG(T, "sellitem");

	if (NULL == sess->player) {
		sess->send_notify("没有角色");
		sess->_trans->disconnect();
		return;
	}

	int serial = msg->sellitem().serial();
	int num = msg->sellitem().num();
	if (!sess->player->can_del_item_by_serial(serial, num)) {
		sess->send_notify("无法删除");
		return;
	}

	sess->player->del_item_by_serial(serial, num);
}

void handle_cplayermedal(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	int32_t medalid = player->att(Pl4s_Medal);
	const tagProtoMedal* medal_proto = ARProto(Medal, medalid);
	if (NULL == medal_proto) {
		sess->send_notify("无效当前勋章");
		return;
	}

	if (ARProto(Medal, medalid+1) == NULL) {
		sess->send_notify("已经是最高级");
		return;
	}

	if (player->att(Pl4s_TaskPage) < medal_proto->task_page) {
		sess->send_notify("任务篇章不够");
		return;
	}

	player->att(Pl4s_Medal, medalid + 1);
	auto info = g_playermgr->infos.get(player->id);
	if (NULL != info)
		info->medal = medalid + 1;
}

void handle_cequiplevelup(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	auto equiplevelup = msg->equiplevelup();
	int32_t pos = equiplevelup.pos();
	if (pos < 0 || pos > PlayerEquipSlotNum) {
		sess->send_notify("位置无效");
		return;
	}

	equip_t &equip = player->equip_bar[pos];
	auto equiplevel_proto = ARProto(EquipLevel, equip.equip_level);
	if (NULL == equiplevel_proto) {
		sess->send_notify("无效等级");
		return;
	}

	if (equip.enhance_num < equiplevel_proto->enhance_max_num) {
		sess->send_notify("没有到最大强化次数");
		return;
	}

	auto next_proto = ARProto(EquipLevel, equip.equip_level + 1);
	if (NULL == next_proto) {
		sess->send_notify("无效下一级");
		return;
	}

	int32_t consumeid = equiplevel_proto->levelup_consume[pos];
	if (!player->can_consume(consumeid)) {
		sess->send_notify("消耗不足");
		return;
	}

	int min_level = 999;
	for (int i = 0; i < PlayerEquipSlotNum; ++i) {
		min_level = std::min<int>(min_level, player->equip_bar[i].equip_level);
	}

	++equip.equip_level;
	equip.enhance_num = 0;
	equip.chg = true;

	player->consume(consumeid);

	player->calc_equip_mod();

	int new_min_level = 999;
	for (int i = 0; i < PlayerEquipSlotNum; ++i) {
		new_min_level = std::min<int>(new_min_level,
				player->equip_bar[i].equip_level);
	}

	if (min_level != new_min_level) {
		auto playerinfo = g_playermgr->infos.get(player->id);
		if (NULL != playerinfo) {
			playerinfo->equip_level = new_min_level;
		}
		g_chat->broad_scroll(
				fmt_cstr("1,%s,%d", player->att(Plss_Name).c_str(),
						new_min_level));
	}
}

void handle_cequipenhance(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	auto enhance = msg->equipenhance();
	int32_t pos = enhance.pos();
	if (pos < 0 || pos > PlayerEquipSlotNum) {
		sess->send_notify("位置无效");
		return;
	}
	equip_t &equip = player->equip_bar[pos];
	auto equiplevel_proto = ARProto(EquipLevel, equip.equip_level);
	if (NULL == equiplevel_proto) {
		sess->send_notify("无效等级");
		return;
	}

	int32_t consumeid = -1;
	if (equip.enhance_num < equiplevel_proto->enhance_consume.size()) {
		consumeid = equiplevel_proto->enhance_consume[equip.enhance_num];
	}

	if (!player->can_consume(consumeid)) {
		sess->send_notify("消耗不足");
		return;
	}

	if (equip.enhance_num >= equiplevel_proto->enhance_max_num) {
		sess->send_notify("最大强化次数");
		return;
	}

	int32_t item_serial = enhance.item_serial();
	if (!check_item_by_serial(player, item_serial, EIT_EquipEnhance, 1)) {
		sess->send_notify("无效物品");
		return;
	}
	auto item = player->get_bag_item(item_serial);

	player->del_item_by_serial(item_serial, 1);

	player->consume(consumeid);

	if (!prob(item->proto->misc1))
		return;

	++equip.enhance_num;
	equip.chg = true;
	player->calc_equip_mod();
}

void handle_cequipstar(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	auto star = msg->equipstar();
	int32_t pos = star.pos();
	if (pos < 0 || pos >= PlayerEquipSlotNum) {
		sess->send_notify("无效位置");
		return;
	}

	equip_t &equip = player->equip_bar[pos];
	auto equiplevel_proto = ARProto(EquipLevel, equip.equip_level);
	if (NULL == equiplevel_proto) {
		sess->send_notify("无效等级");
		return;
	}

	int32_t min_star = 0;
	int32_t max_star = 0;
	int32_t consumeid = -1;
	if (star.has_money()) {
		consumeid = equiplevel_proto->star_money_consume;
		min_star = equiplevel_proto->star_min_money;
		max_star = equiplevel_proto->star_max_money;
	} else {
		consumeid = equiplevel_proto->star_game_money_consume;
		min_star = equiplevel_proto->star_min_game_money;
		max_star = equiplevel_proto->star_max_game_money;
	}

	if (!player->can_consume(consumeid)) {
		sess->send_notify("消耗不足");
		return;
	}

	for (int i = 0; i < CanSeePetAttNum; ++i)
		equip.star_num[i] = random(min_star, max_star);

	equip.chg = true;

	player->consume(consumeid);

	player->calc_equip_mod();

	sess->stat.add_consume_byid(ECT_EquipStar, -1, consumeid);
}

void handle_cbagextend(client_session_t* sess, cs::CMsg* msg) {
	static int32_t extend_consume[PlayerMaxBagPageNum + 1] {
//      0   1   2   3    4    5    6    7    8    9    10   11   12
			-1, -1, -1, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412 };

	auto player = sess->player;
	if (NULL == player)
		return;

	int32_t pagenum = player->att(Pl4s_BagPageNum);
	if (pagenum >= PlayerMaxBagPageNum) {
		sess->send_notify("已经达到最大页数");
		return;
	}

	int32_t consumeid = extend_consume[pagenum + 1];
	if (!player->can_consume(consumeid)) {
		sess->send_notify("消耗不足");
		return;
	}

	player->consume(consumeid);
	player->att(Pl4s_BagPageNum, pagenum + 1);
}

void handle_cmallbuy(client_session_t* sess, cs::CMsg* msg) {
	int32_t protoid = msg->mallbuy().protoid();

	auto player = sess->player;
	if (NULL == player)
		return;

	const tagProtoItem* item_proto = MAProto(Item, protoid);
	if (NULL == item_proto) {
		sess->send_notify("没有这类物品");
		return;
	}

	if (!player->can_add_item(protoid, 1)) {
		sess->send_notify("空间不足");
		return;
	}

	if (item_proto->buy_consume < 0) {
		sess->send_notify("不出售");
		return;
	}

	if (!player->can_consume(item_proto->buy_consume)) {
		sess->send_notify("消耗不足");
		return;
	}

	player->add_item(protoid, 1);

	player->consume(item_proto->buy_consume);

	sess->stat.add_consume_byid(ECT_BuyItem, item_proto->id,
			item_proto->buy_consume);
}

void handle_cbagsell(client_session_t* sess, cs::CMsg* msg) {
	int32_t protoid = msg->bagsell().protoid();
	int32_t num = msg->bagsell().num();

	auto player = sess->player;
	if (NULL == player)
		return;

	const tagProtoItem* item_proto = MAProto(Item, protoid);
	if (NULL == item_proto) {
		sess->send_notify("没有这类物品");
		return;
	}

	if (!player->can_del_item(protoid, num)) {
		sess->send_notify("数量不足");
		return;
	}

	player->del_item(protoid, num);
	player->att(Pl8s_GameMoney,
			player->att(Pl8s_GameMoney) + num * item_proto->price);
}

void handle_cbaguse(client_session_t* sess, cs::CMsg* msg) {
	int32_t serial = msg->baguse().serial();

	auto player = sess->player;
	if (NULL == player)
		return;

	item_t* item = player->get_bag_item(serial);
	if (NULL == item) {
		sess->send_notify("无此物品");
		return;
	}

	if (item->proto->use_after >= 0) {
		if (item->create_time <= 0) {
			sess->send_notify("invalid create_time");
			return;
		}
		time_t create_time = item->create_time;
		tm tmlocal = *localtime(&create_time);
		tmlocal.tm_hour = 0;
		tmlocal.tm_min = 0;
		tmlocal.tm_sec = 0;
		time_t start = mktime(&tmlocal);
		int day = (g_clock->tt_now - start) / (24 * 60 * 60);
		if (day < item->proto->use_after) {
			sess->send_notify("not now");
			return;
		}
	}

	if (item->proto->payfee_limit > 0) {
		if (player->att(Pl8s_AccPayFee) < item->proto->payfee_limit) {
			sess->send_notify("payfee_limit");
			return;
		}
	}

	switch (item->proto->type) {
	case EIT_GiftBag:
		if (!check_item_by_serial(player, serial, EIT_GiftBag, 1)) {
			sess->send_notify("无效物品");
			return;
		}

		if (item->proto->misc1 >= 0)
			player->reward(item->proto->misc1);

		if (item->proto->misc2 >= 0) {
			int num = 0;
			int32_t newbook_protoid = pick_one(item->proto->misc2, &num);
			if (newbook_protoid >= 0)
				player->add_item(newbook_protoid, num);
		}

		player->del_item_by_serial(serial, 1);
		break;
	case EIT_ExpRate: {
		if (!check_item_by_serial(player, serial, EIT_ExpRate, 1)) {
			sess->send_notify("无效物品");
			return;
		}
		int32_t cur_fin = player->att(Pl4s_ExpRateFinTime);
		if (g_clock->tt_now > cur_fin) {
			cur_fin = g_clock->tt_now;
		}

		auto item = player->get_bag_item(serial);
		cur_fin += item->proto->misc1 * 60 * 60;

		player->att(Pl4s_ExpRateFinTime, cur_fin);

		player->del_item_by_serial(serial, 1);
	}
		break;
	case EIT_Power: {
		if (!check_item_by_serial(player, serial, EIT_Power, 1)) {
			sess->send_notify("无效物品");
			return;
		}
		int32_t power = player->att(Pl4s_Power);
		auto item = player->get_bag_item(serial);

		player->att(Pl4s_Power, power + item->proto->misc1);

		player->del_item_by_serial(serial, 1);
	}
		break;
	case EIT_Vip: {
		if (!check_item_by_serial(player, serial, EIT_Vip, 1)) {
			sess->send_notify("无效物品");
			return;
		}

		if (item->proto->misc1 <= 0 || item->proto->misc1 > 3) {
			sess->send_notify("无效vip等级");
			return;
		}

		player->att(Pl4s_VipLevel, item->proto->misc1);
		player->att(Pl4s_VipFinishDate,
		g_clock->tt_now + item->proto->misc2 * 60 * 60 * 24);

		player->del_item_by_serial(serial, 1);

		if (item->proto->misc2 >= g_game_cfg->BroadVipDay)
			g_chat->broad_scroll(
					fmt_cstr("3,%s,%d", player->att(Plss_Name).c_str(),
							item->proto->id));
	}
		break;
	case EIT_Zhubo: {
		if (!check_item_by_serial(player, serial, EIT_Zhubo, 1)) {
			sess->send_notify("无效物品");
			return;
		}

		player->n.role = Zhubo;
		player->del_item_by_serial(serial, 1);
	}
		break;
	}
}

void handle_cbagprepare(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	map<int32_t, pair<int32_t, int32_t> > proto_map;
	player->bag.for_each([&proto_map](int32_t serial, item_t* &item) {
		pair<int32_t, int32_t> &one = proto_map[item->proto->id];
		++one.first;
		one.second += item->num;
	});

	bool modified = false;

	for (auto itr = proto_map.begin(); itr != proto_map.end(); ++itr) {
		if (itr->second.first <= 1)
			continue;

		player->del_item(itr->first, itr->second.second);
		player->add_item(itr->first, itr->second.second);
		modified = true;
	}

	if (!modified)
		sess->send_notify("");
}

void handle_ccomposestone(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	auto compose = msg->composestone();

	int32_t protoid = compose.stone_protoid();
	if (!player->can_del_item(protoid, 3)) {
		sess->send_notify("强化石不够");
		return;
	}

	const tagProtoItem* stone_proto = MAProto(Item, protoid);
	if (NULL == stone_proto) {
		sess->send_notify("无效物品");
		return;
	}

	const tagProtoComposeStone* compose_proto = ARProto(ComposeStone,
			stone_proto->misc2);
	if (NULL == compose_proto) {
		sess->send_notify("无效强化石等级");
		return;
	}

	int32_t success_prob = compose_proto->gamemoney_prob;
	int32_t consumeid = compose_proto->gamemoney_consume;
	if (compose.has_money() && compose.money()) {
		success_prob = compose_proto->money_prob;
		consumeid = compose_proto->money_consume;
	}

	if (!player->can_consume(consumeid)) {
		sess->send_notify("消耗不足");
		return;
	}

	if (!player->can_add_item(compose_proto->next_stone_protoid, 1)) {
		sess->send_notify("背包空间不足");
		return;
	}

	player->consume(consumeid);
	player->del_item(protoid, 3);
	player->att(Pl4s_AliveCnt8Compose, player->att(Pl4s_AliveCnt8Compose) + 1);

	if (prob(success_prob)) {
		player->add_item(compose_proto->next_stone_protoid, 1);
	}

	sess->stat.add_consume_byid(ECT_ComposeStone, -1, consumeid);
}

void handle_ccomposeskill(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	auto compose = msg->composeskill();
	int32_t composeitem = -1;
	if (!check_item_by_type(player, EIT_Compose, 1, &composeitem)) {
		sess->send_notify("无魔术棒");
		return;
	}

	set<int32_t> to_del;
	int family = -1;
	for (int i = 0; i < compose.skillbook_size(); ++i) {
		int32_t serial = compose.skillbook(i);
		item_t* book = player->get_bag_item(serial);
		if (NULL == book) {
			sess->send_notify("无此技能书");
			return;
		}
		const tagProtoSkill* skill_proto = MAProto(Skill, book->proto->misc1);
		if (NULL == skill_proto) {
			sess->send_notify("无效技能");
			return;
		}

		if (i == 0)
			family = skill_proto->family;

		if (family != skill_proto->family) {
			sess->send_notify("无效技能系别");
			return;
		}

		if (to_del.count(serial) > 0) {
			sess->send_notify("重复技能");
			return;
		}
		to_del.insert(serial);
	}

	if (!EPetProperty_IsValid(family)) {
		sess->send_notify("无效技能系别");
		return;
	}

	if (to_del.size() < 2) {
		sess->send_notify("技能数不够");
		return;
	}

	const tagProtoPetFamily* family_proto = ARProto(PetFamily, family);
	if (NULL == family_proto) {
		sess->send_notify("无效技能系别");
		return;
	}

	int32_t newbook_protoid = pick_one(family_proto->compose_reward_skill_set);
	if (newbook_protoid >= 0)
		player->add_item(newbook_protoid, 1);

	for_each<int32_t>(to_del, [player](int32_t serial) {
		player->del_item_by_serial(serial, 1);
	});

	player->del_item(composeitem, 1);


	player->att(Pl4s_AliveCnt8Compose, player->att(Pl4s_AliveCnt8Compose) + 1);
}
void handle_ccomposemagic(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	auto compose = msg->composemagic();
	int32_t composeitem = -1;
	if (!check_item_by_type(player, EIT_Compose, 1, &composeitem)) {
		sess->send_notify("无魔术棒");
		return;
	}

	set<int32_t> to_del;
	int family = -1;
	int level = -1;
	for (int i = 0; i < compose.magic_size(); ++i) {
		int32_t serial = compose.magic(i);
		item_t* book = player->get_bag_item(serial);
		if (NULL == book) {
			sess->send_notify("无此技能书");
			return;
		}
		const tagProtoSkill* skill_proto = MAProto(Skill, book->proto->misc1);
		if (NULL == skill_proto) {
			sess->send_notify("无效技能");
			return;
		}

		if (skill_proto->family >= 0) {
			sess->send_notify("无效技能系别");
			return;
		}

		if (i == 0)
			level = book->proto->misc2;
		if (level != book->proto->misc2) {
			sess->send_notify("魔法印记级别不对");
			return;
		}

		if (to_del.count(serial) > 0) {
			sess->send_notify("重复技能");
			return;
		}

		to_del.insert(serial);
	}

	if (to_del.size() != 2) {
		sess->send_notify("魔法印记不够");
		return;
	}

	const tagProtoMagicLevel* magiclevel_proto = ARProto(MagicLevel, level);
	if (NULL == magiclevel_proto) {
		sess->send_notify("魔法印记等级无效");
		return;
	}

	int32_t newbook_protoid = pick_one(
			magiclevel_proto->compose_reward_magic_set);
	if (newbook_protoid >= 0)
		player->add_item(newbook_protoid, 1);

	for_each<int32_t>(to_del, [player](int32_t serial) {
		player->del_item_by_serial(serial, 1);
	});

	player->del_item(composeitem, 1);

	player->att(Pl4s_AliveCnt8Compose, player->att(Pl4s_AliveCnt8Compose) + 1);
}

void handle_cfricombat(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	int64_t playerid = msg->fricombat().playerid();
	if (player->friends.count(playerid) == 0) {
		sess->send_notify("不是好友");
		return;
	}

	g_playermgr->player_load(playerid,
			[player](sp_player_t friplayer) {
				if (!player->sess)
				return;

				int combatnum = player->att(Pl4s_FriendCombatNum);

				CREATE_CS_SMSG(SFriCombat, fricombat);
				if (friplayer) {
					bool has_combated = player->friends_combated.count(friplayer->id) > 0;
					if (!has_combated) {
						player->friends_combated.insert(friplayer->id);
						player->friends_combated_added.insert(friplayer->id);
					}

					int leveldiff = 1;
					auto combat = fricombat->mutable_combat();
					g_combatcookie(player.get(), friplayer.get(), 0);
					bool res = g_combat(player.get(), friplayer.get(), combat);
					if (res) {
						int diff = player->att(Pl4s_CombatForce) - friplayer->att(Pl4s_CombatForce);
						int absdiff = abs(diff);
						if (absdiff > g_game_cfg->FriCombatLevelDiff) {
							if (diff > 0)
							leveldiff = 2;
							else
							leveldiff = 0;
						} else {
							leveldiff = 1;
						}
					} else {
						leveldiff = 3;
					}
					if (!has_combated && combatnum < g_game_cfg->FriendCombatRewardNum) {
						player->reward(g_game_cfg->FriCombatReward[leveldiff], combat->mutable_reward());
					}
					combatnum++;
				}
				player->sess->send_msg(___msg);
				player->att(Pl4s_AliveCnt3FriCombat, player->att(Pl4s_AliveCnt3FriCombat) + 1);
				player->att(Pl4s_FriendCombatNum, combatnum);
				player->att(Pl4s_CntFriendCombat, player->att(Pl4s_CntFriendCombat) + 1);
			});
}
void handle_cfrigift(client_session_t* sess, cs::CMsg* msg) {
	// 使用物品 发邮件
}
void handle_cfriremove(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	int64_t playerid = msg->friremove().playerid();
	if (player->friends.count(playerid) <= 0) {
		sess->send_notify("不是好友");
		return;
	}

	player->friends.erase(playerid);
	player->friends_chged.insert(playerid);
}
void handle_cfriadd(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	if (player->friends.size() >= MaxFriendNum) {
		sess->send_notify("好友达到最大人数");
		return;
	}

	int64_t playerid = msg->friadd().playerid();
	if (player->id == playerid) {
		sess->send_notify("can not add self");
		return;
	}

	if (g_playermgr->infos.count(playerid) <= 0) {
		sess->send_notify("查无此人");
		return;
	}

	if (player->friends.count(playerid) > 0) {
		sess->send_notify("已经是好友");
		return;
	}

	player->friends.insert(playerid);
	player->friends_chged.insert(playerid);

}
void handle_cfrifind(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	const simple_player_info_t* info = NULL;
	auto itr = g_playermgr->name_table.find(msg->frifind().name());
	if (itr != g_playermgr->name_table.end())
		info = g_playermgr->infos.get(itr->second);

	CREATE_CS_SMSG(SFriRefresh, frirefresh);
	if (info != NULL) {
		auto oinfo = frirefresh->add_info();
		fill_osimpleplayerinfo(info, oinfo);
	}
	sess->send_msg(___msg);
	;

}

void handle_csimpleplayerinfo(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	const auto &cmsg = msg->simpleplayerinfo();
	int size = cmsg.playerid_size();

	CREATE_CS_SMSG(SSimplePlayerInfo, simpleplayerinfo);
	for (int i = 0; i < size; ++i) {
		auto info = g_playermgr->infos.get(cmsg.playerid(i));
		if (NULL != info) {
			fill_osimpleplayerinfo(info, simpleplayerinfo->add_info());
		}
	}
	sess->send_msg(___msg);
	;
}

void handle_cplayerinfo(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	const auto &cmsg = msg->playerinfo();

	int64_t playerid = cmsg.playerid();
	g_playermgr->player_load(playerid,
			[player](sp_player_t queried_player) {
				if (NULL == player->sess)
				return;

				CREATE_CS_SMSG(SPlayerInfo, playerinfo);
				if (NULL != queried_player) {
					OPlayerInfo* info = playerinfo->mutable_info();
					info->set_playerid(queried_player->id);
					info->set_name(queried_player->att(Plss_Name));
					info->set_sex(queried_player->att(Pl4s_PlayerSex));
					info->set_combat_force(queried_player->att(Pl4s_CombatForce));
					info->set_medal(queried_player->att(Pl4s_Medal));
					info->set_guild_name(-1);
					info->set_card(queried_player->pethas.size() + queried_player->levelhas.get_1num());
					for (int j=0; j<CanSeePetAttNum; ++j)
					info->add_att(queried_player->att(EPlAtt4Save(j + Pl4s_EquipAttBegin)));
					for (int i=0; i<PlayerEquipSlotNum; ++i)
					info->add_equip_level(queried_player->equip_bar[i].equip_level);
					for (int i=0; i<PlayerCombatCSlotNum; ++i) {
						int32_t serial = queried_player->att(EPlAtt4Save(i+Pl4s_CombatBegin));
						if (serial < 0) {
							info->add_pet_proto(-1);
							continue;
						}
						pet_t* pet = queried_player->find_pet(serial);
						if (NULL == pet) {
							info->add_pet_proto(-1);
							continue;
						}

						info->add_pet_proto(pet->proto->id);
					}
				}
				player->sess->send_msg(___msg);
			});

}

void handle_cbuyarenacombatnum(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	int buynum = player->att(Pl4s_BuyArenaCombatNumNum);
	if (buynum >= 10) {
		sess->send_notify("次数超了");
		return;
	}

	if (!player->can_consume(g_game_cfg->BuyArenaCombatConsume)) {
		sess->send_notify("消耗不足");
		return;
	}

	int combatnum = player->att(Pl4s_ArenaCombatRemainNum);
	player->att(Pl4s_ArenaCombatRemainNum, combatnum + 1);

	player->consume(g_game_cfg->BuyArenaCombatConsume);
	player->att(Pl4s_BuyArenaCombatNumNum, buynum + 1);

	sess->stat.add_consume_byid(ECT_ArenaCombatNum, -1,
	g_game_cfg->BuyArenaCombatConsume);
}

void handle_carenarefresh(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	if (!g_arena->rewarding() && !g_arena->is_full()
			&& !g_arena->has_player(player->id))
		g_arena->first_insert(player->id);

	list<int64_t> lst;
	int32_t index = g_arena->query(player->id, lst);

	if (index < 0)
		index = RANK_SIZE;
	player->att(Pl4n_ArenaIndex, index);

	CREATE_CS_SMSG(SArenaRefresh, arenarefresh);
	arenarefresh->set_rewarding(g_arena->rewarding());
	for (auto itr = lst.begin(); itr != lst.end(); ++itr) {
		auto info = g_playermgr->infos.get(*itr);
		if (info == NULL)
			continue;

		auto oinfo = arenarefresh->add_info();
		fill_osimpleplayerinfo(info, oinfo);
	}
	for (auto itr = player->arenalog.begin(); itr != player->arenalog.end();
			++itr) {
		auto olog = arenarefresh->add_log();
		olog->set_attacker(itr->attacker);
		olog->set_defender(itr->defender);
		olog->set_win(itr->win);
		olog->set_index(itr->index);
	}
	player->sess->send_msg(___msg);
	;
}

void handle_carenacombat(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	if (g_arena->rewarding()) {
		sess->send_notify("正在奖励");
		return;
	}

	int32_t combatremainnum = player->att(Pl4s_ArenaCombatRemainNum);
	if (combatremainnum <= 0) {
		sess->send_notify("当日最大次数已到");
		return;
	}

	int64_t hisid = msg->arenacombat().playerid();
	bool crit = msg->arenacombat().crit();
	if (crit) {
		if (!player->can_consume(g_game_cfg->ArenaCritCombatConsume)) {
			sess->send_notify("消耗不够");
			return;
		}
	}

	if (hisid < 0) {
		if (crit) {
			int selfrank = player->att(Pl4n_ArenaIndex) + 1;
			int hisrank = selfrank - 100;
			if (hisrank <= 100) {
				sess->send_notify("不能打100名以内");
				return;
			}
			if (hisrank > 0) {
				hisid = g_arena->get_playerid(hisrank - 1);
			}
		}
	}

	if (hisid < 0) {
		CREATE_CS_SMSG(SArenaCombat, arenacombat);
		sess->send_msg(___msg);
		return;
	}

	if (!g_arena->is_before(player->id, hisid)) {
		CREATE_CS_SMSG(SArenaCombat, arenacombat);
		sess->send_msg(___msg);
		return;
	}

	g_playermgr->player_load(hisid,
			[crit, player, combatremainnum](sp_player_t oppo) {
				if (NULL == player->sess)
				return;

				if (!oppo) {
					player->sess->send_notify("无此玩家");
					return;
				}

				if (crit) {
					player->consume(g_game_cfg->ArenaCritCombatConsume);
					player->sess->stat.add_consume_byid(ECT_ArenaCrit, -1, g_game_cfg->ArenaCritCombatConsume);
				}

				player->att(Pl4s_ArenaCombatRemainNum, combatremainnum-1);
				int32_t rewardstep = player->att(Pl4s_ArenaRewardStep);
				if (rewardstep < 5) {
					++rewardstep;
					player->att(Pl4s_ArenaRewardStep, rewardstep);
				}

				CREATE_CS_SMSG(SArenaCombat, arenacombat);
				if (g_arena->is_before(player->id, oppo->id)) {
					auto combat = arenacombat->mutable_combat();
					g_combatcookie(player.get(), oppo.get(), 0);
					bool win = g_combat(player.get(), oppo.get(), combat);
					if (win) {
						int32_t index = g_arena->insert(player->id, oppo->id);
						player->att(Pl4n_ArenaIndex, index);

						// 奖励
						player->reward(g_game_cfg->ArenaCombatRewardWin, combat->mutable_reward());
						if (index < g_game_cfg->BroadArenaCombatCastLimit) {
							g_chat->broad_scroll(fmt_cstr("2,%s,%s,%d",
											player->att(Plss_Name).c_str(),
											oppo->att(Plss_Name).c_str(),
											index));
						}
					} else {
						// 奖励
						player->reward(g_game_cfg->ArenaCombatRewardLose, combat->mutable_reward());
					}

					int myidx = g_arena->get_index(player->id);
					int heidx = g_arena->get_index(oppo->id);
					player_t::arenalog_t log;
					log.attacker = player->att(Plss_Name);
					log.defender = oppo->att(Plss_Name);
					log.win = win;

					log.index = myidx;
					player->push_log(log);
					log.index = heidx;
					oppo->push_log(log);

					player->att(Pl4s_AliveCnt2ArenaCombat, player->att(Pl4s_AliveCnt2ArenaCombat) + 1);
					player->att(Pl4s_CntArenaCombat, player->att(Pl4s_CntArenaCombat) + 1);
				}
				player->sess->send_msg(___msg);
			});
}

void handle_cranklist(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	const auto &cranklist = msg->ranklist();
	CREATE_CS_SMSG(SRankList, ranklist);
	ranklist->set_start(cranklist.start());
	ranklist->set_type(cranklist.type());

	switch (cranklist.type()) {
	case ERT_Player: {
		rank_mgr::player_rank_list_t* rank_list = NULL;
		unordered_map<int64_t, int>* rank_rank = NULL;
		if (player->att(Pl4s_Medal) < g_game_cfg->LowRankMedal) {
			rank_rank = &g_rank->player_low_rank;
			rank_list = &g_rank->player_low_list;
		} else if (player->att(Pl4s_Medal) < g_game_cfg->MidRankMedal) {
			rank_rank = &g_rank->player_mid_rank;
			rank_list = &g_rank->player_mid_list;
		} else {
			rank_rank = &g_rank->player_rank;
			rank_list = &g_rank->player_list;
		}

		if ((*rank_rank).count(player->id) > 0)
			player->att(Pl4n_PlayerRank, (*rank_rank)[player->id]);
		else
			player->att(Pl4n_PlayerRank, -1);

		for (int i = 0; i < cranklist.num(); ++i) {
			int32_t idx = i + cranklist.start();
			if (idx >= rank_list->size() || idx < 0) {
				break;
			}

			const auto &rankitem = (*rank_list)[idx];

			ORankPlayer* rank_player = ranklist->add_player_list();

			rank_player->set_playerid(rankitem.playerid);
			rank_player->set_combat_force(rankitem.player_combat_force);
			auto info = g_playermgr->infos.get(rankitem.playerid);
			if (NULL != info) {
				rank_player->set_sex(info->sex);
				rank_player->set_equip_level(info->equip_level);
				rank_player->set_name(info->name);
				rank_player->set_guildid(-1);
			} else {
				rank_player->set_sex(EPS_Other);
				rank_player->set_equip_level(0);
				rank_player->set_name("invalid");
				rank_player->set_guildid(-1);
			}
		}
		break;
	}
	case ERT_Pet: {
		if (g_rank->pet_rank.count(player->id) > 0)
			player->att(Pl4n_PetRank, g_rank->pet_rank[player->id]);
		else
			player->att(Pl4n_PetRank, -1);

		for (int i = 0; i < cranklist.num(); ++i) {
			int32_t idx = i + cranklist.start();
			if (idx >= g_rank->pet_list.size() || idx < 0) {
				break;
			}

			const auto &rankitem = g_rank->pet_list[idx];

			ORankPet* rank_pet = ranklist->add_pet_list();

			rank_pet->set_serial(rankitem.serial);
			rank_pet->set_name(rankitem.pet_name);
			rank_pet->set_proto(rankitem.protoid);
			rank_pet->set_player_id(rankitem.owner_id);
			rank_pet->set_combat_force(rankitem.pet_combat_force);

			auto info = g_playermgr->infos.get(rankitem.owner_id);
			if (NULL != info)
				rank_pet->set_player_name(info->name);
			else
				rank_pet->set_player_name("invalid");
		}
		break;
	}
	case ERT_Arena: {
		int total = g_arena->get_size();
		for (int i = 0; i < cranklist.num(); ++i) {
			if (i >= total)
				break;

			int64_t playerid = g_arena->get_playerid(i);
			if (playerid < 0)
				continue;

			auto info = g_playermgr->infos.get(playerid);
			if (NULL == info)
				continue;

			ORankPlayer* rank_player = ranklist->add_player_list();

			rank_player->set_playerid(playerid);
			rank_player->set_combat_force(info->total_combat_force);
			rank_player->set_sex(info->sex);
			rank_player->set_equip_level(info->equip_level);
			rank_player->set_name(info->name);
			rank_player->set_guildid(-1);
		}
		break;
	}
	default:

		break;
	}
	sess->send_msg(___msg);
	;

}

void handle_carenagetreward(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	if (player->att(Pl4s_ArenaRewardStep) < 5) {
		sess->send_notify("次数不够");
		return;
	}

	player->att(Pl4s_ArenaRewardStep, 0);
	CREATE_CS_SMSG(SReward, reward);
	player->reward(g_game_cfg->ArenaReward, reward->mutable_reward());
	player->sess->send_msg(___msg);
	;
}

void handle_cmaillist(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	if (player->att(Pl4n_PersonMailNew) < 0
			|| player->att(Pl4n_SystemMailNew) < 0) {
		sess->send_notify("尚未初始化");
		return;
	}

	int32_t start = msg->maillist().start();
	int32_t num = msg->maillist().num();
	if (start < 0 || num <= 0 || start > 100 || num > 100) {
		sess->send_notify("范围不对");
		return;
	}

	auto type = msg->maillist().type();

	g_mail->box_load(player->id,
			[player, start, num, type](db_error_t err, mailbox_t* box) {
				if (player->sess == NULL)
				return;

				if (NULL == box)
				return;

				ptr_map<int32_t, mail_t*>* mail_map = NULL;
				int* last_mail = NULL;
				int* new_mail = NULL;
				if (type == cs::EMT_Person) {
					mail_map = &box->person;
					last_mail = &box->last_person;
					new_mail = &box->new_person;
				} else {
					mail_map = &box->system;
					last_mail = &box->last_system;
					new_mail = &box->new_system;
				}
				int old_last_mail = *last_mail;

				CREATE_CS_SMSG(SMailList, maillist);
				auto itr = mail_map->rbegin();
				for (int i = 0; i < start; ++i) {
					if (itr == mail_map->rend())
					break;

					++itr;
				}
				for (int i=0; i<num; ++i) {
					if (itr == mail_map->rend())
					break;

					if (itr->first > *last_mail) {
						*last_mail = itr->first;
					}

					if (itr->first > old_last_mail) {
						--(*new_mail);
					}

					cs::OMailInfo* oinfo = maillist->add_mailinfo();
					oinfo->set_mailid(itr->first);
					oinfo->set_sender(itr->second->sender);
					oinfo->set_readed(itr->second->readed);
					oinfo->set_rewarded(itr->second->rewarded);

					if (itr->second->type == cs::EMT_Person) {
						auto info = g_playermgr->infos.get(itr->second->sender);
						if (NULL != info)
						oinfo->set_sendername(info->name);
					} else {
						oinfo->set_type((EMailType) itr->second->type);
					}

					++itr;
				}
				player->sess->send_msg(___msg);

				if (type == cs::EMT_Person)
				player->att(Pl4n_PersonMailNew, *new_mail);
				else
				player->att(Pl4n_SystemMailNew, *new_mail);

				if (*last_mail != old_last_mail) {
					box->add_queue(true);
				}
			});
}
void handle_cmailread(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	int32_t mailid = msg->mailread().mailid();
	if (mailid < 0)
		return;

	g_mail->box_load(player->id,
			[player, mailid](db_error_t err, mailbox_t* box) {
				if (NULL == player->sess)
				return;

				mail_t* mail = box->get_mail(mailid);

				CREATE_CS_SMSG(SMailRead, mailread);
				if (NULL != mail) {
					auto omail = mailread->mutable_mail();
					omail->set_mailid(mail->mailid);
					omail->set_date(mail->date);
					omail->set_content(mail->content);
					omail->set_rewardid(mail->rewardid);
					omail->set_rewardpct(mail->rewardpct);
					if (!mail->readed) {
						mail->readed = true;
						box->add_queue(true);
					}
				}
				mailread->set_mailid(mailid);
				player->sess->send_msg(___msg);
			});
}
void handle_cmailsend(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	g_mail->send_person(msg->mailsend().receiver(), player->id,
			msg->mailsend().content(), [player](bool ok) {
				if (NULL == player->sess)
				return;

				CREATE_CS_SMSG(SMailSend, mailsend);
				mailsend->set_ok(ok);
				player->sess->send_msg(___msg);
			});
}
void handle_cmaildel(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	auto mailid = msg->maildel().mailid();
	if (mailid < 0)
		return;

	g_mail->box_load(player->id,
			[player, mailid](db_error_t err, mailbox_t* box) {
				if (NULL == player->sess)
				return;

				if (NULL == box)
				return;

				bool del = false;
				mail_t* mail = box->get_mail(mailid);
				if (NULL != mail) {
					if (mail->type == EMT_Person)
					box->person.erase(mailid);
					else
					box->system.erase(mailid);
					delete mail;
					del = true;
					box->add_queue(true);
				}

				CREATE_CS_SMSG(SMailDel, maildel);
				maildel->set_mailid(mailid);
				maildel->set_ok(del);
				player->sess->send_msg(___msg);
			});
}
void handle_cmailreward(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	int32_t mailid = msg->mailreward().mailid();
	if (mailid < 0)
		return;

	g_mail->box_load(player->id,
			[player, mailid](db_error_t err, mailbox_t* box) {
				if (NULL == player->sess)
				return;

				if (NULL == box)
				return;

				mail_t* mail = box->get_mail(mailid);
				CREATE_CS_SMSG(SMailReward, mailreward);
				mailreward->set_mailid(mailid);
				if (mail && !mail->rewarded) {
					mail->rewarded = true;
					box->add_queue(true);
					player->reward(mail->rewardid,
							mailreward->mutable_reward(),
							mail->rewardpct);
				}
				player->sess->send_msg(___msg);
			});
}

void handle_cchat(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	auto cchat = msg->chat();
	switch (cchat.channel()) {
	case ECC_World: {
		if (g_clock->tt_now < player->valid_worldchat) {
			sess->send_notify("世界聊天太频繁");
			return;
		}
		player->valid_worldchat = g_clock->tt_now + 10;

		CREATE_CS_SMSG(SChat, chat);
		chat->set_senderid(player->id);
		chat->set_name(player->att(Plss_Name));
		chat->set_channel(ECC_World);
		chat->set_content(cchat.content());
		chat->set_idmask(player->att(Pl4s_IdMask));
		if (player->n.role == Zhubo)
			chat->set_vip(4);
		else
			chat->set_vip(player->att(Pl4s_VipLevel));

		if (player->n.forbid) {
			sess->send_msg(___msg);
			return;
		} else {
			g_chat->add_channel(___msg, player->att(Pl4n_Channel));
		}
	}
		break;
	case ECC_Family: {
		break;
	}
	case ECC_Private: {
		sp_player_t to;
		if (cchat.has_toid()) {
			to = g_playermgr->get_player(cchat.toid());
		} else if (cchat.has_toname()) {
			auto id = g_playermgr->name_table.find(cchat.toname());
			if (id->second < 0) {
				sess->send_notify("no suchplayer by name");
				break;
			}
			to = g_playermgr->get_player(cchat.toid());
		} else {
			break;
		}

		if (!to) {
			sess->send_notify("no suchplayer by id");
			break;
		}

		if (!to->sess) {
			sess->send_notify("not online");
			break;
		}

		CREATE_CS_SMSG(SChat, chat);
		chat->set_senderid(player->id);
		chat->set_name(player->att(Plss_Name));
		chat->set_channel(ECC_Private);
		chat->set_content(cchat.content());
		chat->set_vip(player->att(Pl4s_VipLevel));
		chat->set_idmask(player->att(Pl4s_IdMask));

		if (!player->n.forbid) {
			to->sess->send_msg(___msg);
		}
	}
		break;
	}
}

void handle_ctaskreward(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	const auto &vec_page = g_protomgr->vec_page_task;
	int page = player->att(Pl4s_TaskPage);
	if (page < 0 || page >= vec_page.size()) {
		sess->send_notify("page err");
		return;
	}

	int slot = msg->taskreward().slot();
	if (slot > 2 || slot < 0) {
		sess->send_notify("错误slot");
		return;
	}

	uint32_t complete_mask = player->att(Pl4s_TaskCompletedMask);
	if (complete_mask & (1<<slot)) {
		sess->send_notify("已经完成");
		return;
	}

	int32_t taskid = vec_page[page][slot];
	const tagProtoTask* task_proto = MAProto(Task, taskid);
	if (NULL == task_proto) {
		sess->send_notify("no such task:%d", taskid);
		return;
	}

	switch (task_proto->type) {
	case ETT_Boss: {
		auto bosslevel = player->_boss_level_progress.get(task_proto->misc);
		if (NULL == bosslevel || !bosslevel->haspass) {
			sess->send_notify("请先完成boss关卡");
			return;
		}
		break;
	}
	case ETT_Level: {
		if (player->att(Pl4s_VentCurLevel) < task_proto->misc) {
			sess->send_notify("请先完成关卡");
			return;
		}
		break;
	}
	case ETT_Hatch: {
		if (player->att(Pl4s_CntHatchEgg) < task_proto->misc) {
			sess->send_notify("请先氟蛋");
			return;
		}
		player->att(Pl4s_CntHatchEgg, 0);
		break;
	}
	case ETT_SkillLearn: {
		if (player->att(Pl4s_CntPetLearnSkill) < task_proto->misc) {
			sess->send_notify("图鉴数量不够");
			return;
		}
		player->att(Pl4s_CntPetLearnSkill, 0);
		break;
	}
	case ETT_SkillLvlup: {
		bool found = false;
		for (auto &itr : player->pets) {
			for (int i = 0; i < PetSpecSkillNum; ++i) {
				if (i == 4)	// dont deal that skill
					continue;

				auto pet = itr.second;
				int32_t skillid = pet->att(
						EPeAtt4Save(Pe4s_SkillSSlotBegin + i));
				if (skillid < 0)
					continue;

				const tagProtoSkill* skill_proto = MAProto(Skill, skillid);
				if (NULL == skill_proto)
					continue;

				if (skillid % 10 >= task_proto->misc) {
					found = true;
					break;
				}
			}
			if (found)
				break;
		}
		if (!found) {
			sess->send_notify("图鉴数量不够");
			return;
		}
		break;
	}
	case ETT_SkillBookGain: {
		int total_num = 0;
		for (auto itr : player->bag) {
			auto &item = itr.second;
			if (item->proto->type != EIT_PetSkillLearn)
				continue;

			total_num += item->num;
			if (total_num >= task_proto->misc) {
				break;
			}
		}
		if (total_num < task_proto->misc) {
			sess->send_notify("技能书数量不够");
			return;
		}
		break;
	}
	case ETT_SkillLvlupBook: {
		int total_num = 0;
		for (auto itr : player->bag) {
			auto &item = itr.second;
			if (item->proto->type != EIT_PetSkillLevelUp)
				continue;

			total_num += item->num;
			if (total_num >= task_proto->misc) {
				break;
			}
		}
		if (total_num < task_proto->misc) {
			sess->send_notify("技能升级卷数量不够");
			return;
		}
		break;
	}
	case ETT_EnhancePet: {
		if (player->att(Pl4s_CntEnhancePet) < task_proto->misc) {
			sess->send_notify("次数不够");
			return;
		}
		player->att(Pl4s_CntEnhancePet, 0);
		break;
	}
	case ETT_ReleasePet: {
		if (player->att(Pl4s_CntReleasePet) < task_proto->misc) {
			sess->send_notify("次数不够");
			return;
		}
		player->att(Pl4s_CntReleasePet, 0);
		break;
	}
	case ETT_CombatForce: {
		if (player->att(Pl4s_TotalCombatForce) < task_proto->misc) {
			sess->send_notify("战力不够");
			return;
		}
		break;
	}
	case ETT_EquipEnhance: {
		bool found = false;
		for (int i = 0; i < PlayerEquipSlotNum; ++i) {
			if (player->equip_bar[i].enhance_num >= task_proto->misc) {
				found = true;
				break;
			}
		}

		if (!found) {
			sess->send_notify("次数不够");
			return;
		}
		break;
	}
	case ETT_EquipLevelup: {
		bool found = false;
		for (int i = 0; i < PlayerEquipSlotNum; ++i) {
			if (player->equip_bar[i].equip_level >= task_proto->misc) {
				found = true;
				break;
			}
		}

		if (!found) {
			sess->send_notify("次数不够");
			return;
		}
		break;
	}
	case ETT_EquipWash: {
		bool found = false;
		for (int i = 0; i < PlayerEquipSlotNum; ++i) {
			for (int ii = 0; ii < CanSeePetAttNum; ++ii) {
				if (player->equip_bar[i].star_num[ii] >= task_proto->misc) {
					found = true;
					break;
				}
			}
			if (found)
				break;
		}

		if (!found) {
			sess->send_notify("星不够");
			return;
		}
		break;

	}
	case ETT_SpriteExplore: {
		if (player->att(Pl4s_CntSpriteExplore) < task_proto->misc) {
			sess->send_notify("次数不够");
			return;
		}
		player->att(Pl4s_CntSpriteExplore, 0);
		break;
	}

	case ETT_SpriteSummon: {
		if (player->att(Pl4s_CntSpriteSummon) < task_proto->misc) {
			sess->send_notify("次数不够");
			return;
		}
		player->att(Pl4s_CntSpriteSummon, 0);
		break;
	}

	case ETT_ArenaCombat: {
		if (player->att(Pl4s_CntArenaCombat) < task_proto->misc) {
			sess->send_notify("次数不够");
			return;
		}
		player->att(Pl4s_CntArenaCombat, 0);
		break;
	}

	case ETT_PetFeed: {
		if (player->att(Pl4s_CntPetFeed) < task_proto->misc) {
			sess->send_notify("次数不够");
			return;
		}
		player->att(Pl4s_CntPetFeed, 0);
		break;
	}

	case ETT_PetFriStage: {
		bool found = false;
		for (auto &itr : player->pets) {
			if (itr.second->att(Pe4s_FStage) >= task_proto->misc) {
				found = true;
				break;
			}
		}
		if (!found) {
			sess->send_notify("次数不够");
			return;
		}
		break;
	}

	case ETT_FriendCombat: {
		if (player->att(Pl4s_CntFriendCombat) < task_proto->misc) {
			sess->send_notify("次数不够");
			return;
		}
		player->att(Pl4s_CntFriendCombat, 0);
		break;
	}
	}
	player->reward(task_proto->reward);

	complete_mask |= (1<<slot);

	if (complete_mask == 7) {
		player->att(Pl4s_TaskPage, page + 1);
		complete_mask = 0;
	}
	player->att(Pl4s_TaskCompletedMask, complete_mask);
}

void handle_cfrirefresh(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	CREATE_CS_SMSG(SFriRefresh, frirefresh);
	auto itr = g_playermgr->infos.find(player->fri_index);

	int player_num = std::min<int>(100, g_playermgr->infos.size());
	for (int i = 0; i < player_num; ++i) {
		if (itr == g_playermgr->infos.end())
			itr = g_playermgr->infos.begin();

		if (itr->first != player->id
				&& player->friends.count(itr->first) == 0) {
			auto info = frirefresh->add_info();
			fill_osimpleplayerinfo(itr->second, info);
			if (frirefresh->info_size() >= PlayerFriendSearchNum)
				break;
		}
		++itr;
	}
	player->sess->send_msg(___msg);

	player->fri_index = itr->first;
}

void handle_calivereward(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	int rewardnum = player->att(Pl4s_AliveRewardNum);
	const tagProtoAliveReward* proto = ARProto(AliveReward, rewardnum);
	if (NULL == proto) {
		sess->send_notify("无效奖励");
		return;
	}

	if (player->att(Pl4s_AliveNum) < proto->count) {
		sess->send_notify("活跃度不够");
		return;
	}

	player->att(Pl4s_AliveRewardNum, rewardnum + 1);
	player->reward(proto->reward);

	sess->stat.add_event(EET_GainGiftBag, proto->reward);
}

void handle_cchatcombat(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	auto oppoid = msg->chatcombat().opposite();
	if (oppoid < 0)
		return;

	g_playermgr->player_load(oppoid,
			[player](sp_player_t oppo) {
				if (NULL == player->sess)
				return;

				if (NULL == oppo)
				return;

				CREATE_CS_SMSG(SChatCombat, chatcombat);
				g_combatcookie(player.get(), oppo.get(), 0);
				bool win = g_combat(player.get(), oppo.get(), chatcombat->mutable_combat());
				player->sess->send_msg(___msg);

				{
					CREATE_CS_SMSG(SChat, chat);
					chat->set_channel(ECC_Combat);
					if (win) {
						chat->set_content(fmt_cstr("%s,%s", player->att(Plss_Name).c_str(), oppo->att(Plss_Name).c_str()));
					} else {
						chat->set_content(fmt_cstr("%s,%s", oppo->att(Plss_Name).c_str(), player->att(Plss_Name).c_str()));
					}
					g_chat->add_channel(___msg, player->att(Pl4n_Channel));
				}
			});
}

void handle_cpetinfo(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	int64_t playerid = msg->petinfo().playerid();
	int32_t pet_serial = msg->petinfo().pet_serial();

	if (playerid < 0 || pet_serial < 0)
		return;

	g_playermgr->player_load(playerid, [player, pet_serial](sp_player_t pp) {
		if (NULL == player->sess)
		return;

		if (NULL == pp)
		return;

		pet_t* pet = pp->find_pet(pet_serial);
		CREATE_CS_SMSG(SPetInfo, petinfo);
		petinfo->set_playerid(pp->id);
		if (NULL != pet)
		pet->fill_opet(petinfo->mutable_pet());
		player->sess->send_msg(___msg);
	});
}

void handle_cdailygiftget(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	int hasgot = player->att(Pl4s_DailyGiftGet);
	if (hasgot > 0) {
		sess->send_notify("已领取");
		return;
	}

	int itemid = -1;
	int days = player->att(Pl4s_ContinueOnlineDayNum);
	switch (days) {
	case 0:
		break;
	case 1:
		itemid = g_game_cfg->DailyOnlineGift[0];
		break;
	case 2:
		itemid = g_game_cfg->DailyOnlineGift[1];
		break;
	default:
		itemid = g_game_cfg->DailyOnlineGift[2];
		break;
	}

	if (itemid < 0) {
		sess->send_notify("无奖励");
		return;
	}

	if (!player->can_add_item(itemid, 1)) {
		sess->send_notify("背包满");
		return;
	}

	player->add_item(itemid, 1);
	player->att(Pl4s_DailyGiftGet, 1);
}

void handle_cdailyvipgiftget(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	int hasgot = player->att(Pl4s_DailyVipGiftGet);
	if (hasgot > 0) {
		sess->send_notify("已领取");
		return;
	}

	int vip = player->att(Pl4s_VipLevel);
	if (vip <= 0) {
		sess->send_notify("不是vip");
		return;
	}

	int32_t itemid = -1;
	switch (vip) {
	case 1:
		itemid = g_game_cfg->DailyVipGift[0];
		break;
	case 2:
		itemid = g_game_cfg->DailyVipGift[1];
		break;
	case 3:
		itemid = g_game_cfg->DailyVipGift[2];
		break;
	default:
		break;
	}

	if (itemid < 0) {
		sess->send_notify("无奖励");
		return;
	}

	if (!player->can_add_item(itemid, 1)) {
		sess->send_notify("背包满");
		return;
	}

	player->add_item(itemid, 1);
	player->att(Pl4s_DailyVipGiftGet, 1);
}

void handle_cmoneyget(client_session_t* sess, cs::CMsg* msg) {
	if (g_clock->tt_now >= g_game_cfg->MoneyGetFinish) {
		sess->send_notify("未开启");
		return;
	}

	auto player = sess->player;
	if (NULL == player)
		return;

	int32_t cur = player->att(Pl4s_MoneyGetCur);
	if (cur < 0) {
		sess->send_notify("无效MoneyGetCur");
		return;
	}

	const tagProtoMoneyGet* proto = ARProto(MoneyGet, cur);
	if (NULL == proto) {
		sess->send_notify("无效天使大方送proto");
		return;
	}

	int64_t money = player->att(Pl8s_Money);
	if (money < proto->consume) {
		sess->send_notify("钱不够");
		return;
	}

	money -= proto->consume;
	money += random(proto->gain_min, proto->gain_max);
	++cur;

	player->att(Pl8s_Money, money);
	player->att(Pl4s_MoneyGetCur, cur);
}



struct _luckystar_item_st {
	int item;
	int num;
};


static const _luckystar_item_st*
_get_lucky_star_items(int seed, int* index) {
	static _luckystar_item_st _items[16] = {};
	static int32_t _probs[16] = {0};
	static int _last_seed = -1;
	static int _inited = 0;

	if (_inited == 0 || _last_seed != seed) {
		if (_inited == 0) {
			_inited = 1;

			for (int i=0; i<16; ++i) {
				int group = (i%4==0?0:1);
				_probs[i] = g_game_cfg->LuckyStarGroupItemProb[group];
			}
		}

		_last_seed = seed;
		unsigned int tmpseed = seed;

		map<int32_t, vector<pair<int32_t, int32_t>>> map_lucky_star;
		for (auto itr : g_protomgr->_arrProtoLuckyStar)
			map_lucky_star[itr.group].push_back(make_pair(itr.itemid, itr.num));

		for (int i=0; i<16; ++i) {
			int group = (i%4==0?0:1);

			int len = map_lucky_star[group].size();
			int randnum = my_rand_r(&tmpseed) % len;
			_items[i].item = map_lucky_star[group][randnum].first;
			_items[i].num = map_lucky_star[group][randnum].second;

			map_lucky_star[group][randnum] = map_lucky_star[group][len-1];
			map_lucky_star[group].pop_back();
		}
	}

    int32_t rand_num = random(0, 10000-1);
    for (int i=0; i<16; ++i) {
        rand_num -= _probs[i];
        if (rand_num < 0) {
            *index = i;
            break;
        }
    }

	return _items;
}

void handle_cluckystar(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	int32_t cur = player->att(Pl4s_LuckyStarNum);
	if (cur < 0 || cur >= g_game_cfg->LuckyStarNum) {
		sess->send_notify("无效LuckyStarNum");
		return;
	}

	int32_t consume_money = g_game_cfg->LuckyStarMoney[cur];
	int money = player->att(Pl8s_Money);
	if (money < consume_money) {
		sess->send_notify("天使比不足");
		return;
	}

	money -= consume_money;
	++cur;

	int idx = 0;
	int32_t item = -1;
	int32_t num = 0;

	const _luckystar_item_st* items = _get_lucky_star_items(g_svr.LuckyStarSeed, &idx);
	if (idx >= 0 && idx < 16) {
		item = items[idx].item;
		num = items[idx].num;

		player->add_item(item, num);
	}

	player->att(Pl4s_LuckyStarNum, cur);
	player->att(Pl8s_Money, money);
	player->att(Pl4n_LuckyStarIndex, idx);
	player->n4.chged.set(Pl4n_LuckyStarIndex);

	if (g_game_cfg->BroadLuckyStarIndex[idx % 4]) {
		g_chat->broad_scroll(
				fmt_cstr("4,%s,%d", player->att(Plss_Name).c_str(), item));
	}

	sess->stat.add_consume_byval(ECT_LuckyStarNum, -1, consume_money);

	player->att(Pl4s_WeeklyCnt5LuckyStar,
			player->att(Pl4s_WeeklyCnt5LuckyStar) + 1);
}

void handle_cusecode(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	if (player->n.code_trytime >= 20)
		return;

	++player->n.code_trytime;
	g_bill->use_code(sess, msg->usecode().code());
}

void handle_cworldbosscombat(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	bool refresh = msg->worldbosscombat().has_refreshcb()
			&& msg->worldbosscombat().refreshcb();

	int32_t cd_consumeid = -1;
	if (g_clock->tt_now < player->att(Pl4s_NextWorldBossCombatTime)) {
		if (!refresh) {
			sess->send_notify("cding");
			return;
		}
		cd_consumeid = g_game_cfg->WorldBossCombatCDConsume;
		if (!player->can_consume(cd_consumeid)) {
			sess->send_notify("consume not enough");
			return;
		}
	}

	if (g_worldboss->status != world_boss_t::COMBATING) {
		sess->send_notify("not combating");
		return;
	}

	if (g_worldboss->cur_hp <= 0) {
		sess->send_notify("cur_hp 0");
		return;
	}

	auto data = g_worldboss->data.get(player->id);
	if (!data) {
		data = make_shared<world_boss_t::rank_item_t>();
		data->name = player->att(Plss_Name);
		data->playerid = player->id;
		data->combat_time = 0;
		g_worldboss->data.set(data->playerid, data);
	}

	assert(data);

	if (data->combat_time >= g_game_cfg->WorldBossCombatTime) {
		sess->send_notify("次数不足");
		return;
	}

	if (!player->can_consume(g_game_cfg->WorldBossCombatConsume)) {
		sess->send_notify("消耗不足");
		return;
	}

	if (cd_consumeid >= 0)
		player->consume(cd_consumeid);

	player->consume(g_game_cfg->WorldBossCombatConsume);

	CREATE_CS_SMSG(SWorldBossCombat, worldbosscombat);
	int32_t old_hp = g_worldboss->cur_hp;
	g_combatcookie(player.get(), NULL, 0);
	g_combat((unit_t*) player.get(), (unit_t*) g_worldboss->boss.get(),
			worldbosscombat->mutable_combat(), &g_worldboss->cur_hp);
	data->damage += (old_hp - g_worldboss->cur_hp);
	++data->combat_time;

	worldbosscombat->set_self_damage(data->damage);

	if (g_worldboss->cur_hp <= 0) {
		g_worldboss->last_attack = player->id;
	}
	g_worldboss->rank_chg = true;
	int32_t level = g_worldboss->level - 20;
	if (level >= 0 && level < 31) {
		int32_t rewardid = g_game_cfg->WorldBossCombatReward[level];
		if (rewardid >= 0)
			player->reward(rewardid,
					worldbosscombat->mutable_combat()->mutable_reward());
	}

	sess->send_msg(___msg);

	player->att(Pl4s_NextWorldBossCombatTime,
	g_clock->tt_now + g_game_cfg->WorldBossCombatCDSec);
}

void handle_cworldbossdata(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	assert(g_worldboss->boss && !g_worldboss->boss->pets.empty());

	CREATE_CS_SMSG(SWorldBossData, worldbossdata);
	worldbossdata->set_started(g_worldboss->status == world_boss_t::COMBATING);
	worldbossdata->set_hp(g_worldboss->cur_hp);
	worldbossdata->set_max_hp(
	g_worldboss->boss->pets.begin()->second->att(Pe4s_Att1));
	worldbossdata->set_bossid(g_worldboss->boss_levelid);

	if (g_worldboss->last_attack >= 0) {
		auto info = g_playermgr->infos.get(g_worldboss->last_attack);
		if (info) {
			worldbossdata->set_last_attack(info->name);
		}
	}
	auto data = g_worldboss->data.get(player->id);
	if (data) {
		worldbossdata->set_self_rank(data->rank);
		worldbossdata->set_combat_time(data->combat_time);
	}
	for (int i = 0; i < 8 && i < g_worldboss->rank.size(); ++i) {
		auto rank_item = g_worldboss->rank[i];
		auto rank = worldbossdata->add_rank();
		rank->set_name(rank_item->name);
		rank->set_damage(rank_item->damage);
	}

	sess->send_msg(___msg);
}

void handle_cweeklycntreward(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	auto type = msg->weeklycntreward().type();
	if (!CWeeklyCntReward_RewardType_IsValid(type)) {
		sess->send_notify("无效 类型");
		return;
	}

	int index = type;
	assert(index >= 0 && index < WeeklyCntTypeNum);

	int rewardmask = player->att(Pl4s_WeeklyRewardMask);
	if (rewardmask & (1 << index)) {
		sess->send_notify("已经领取过");
		return;
	}

	auto cntslot = EPlAtt4Save(Pl4s_WeeklyCntBegin + index);
	int count = player->att(cntslot);
	int need = g_game_cfg->WeeklyCntRewardCnt[index];
	if (count < need) {
		sess->send_notify("数量不够");
		return;
	}

	int32_t rewardid = g_game_cfg->WeeklyCntRewardId[index];
	if (rewardid < 0) {
		sess->send_notify("奖励无效");
		return;
	}

	player->att(Pl4s_WeeklyRewardMask, rewardmask | 1 << index);
	player->reward(rewardid, NULL, 1.0f);
}

void handle_cdailypower(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	int this_min = g_clock->tm_now.tm_hour * 60 + g_clock->tm_now.tm_min;
	int index = 0;
	for (; index < 2; ++index) {
		if (this_min >= g_game_cfg->DailyPowerStartMin[index]
				&& this_min < g_game_cfg->DailyPowerFinishMin[index])
			break;
	}

	if (index >= 2) {
		sess->send_notify("不在时间");
		return;
	}

	int mask = player->att(Pl4s_DailyPowerMask);
	if (mask & (1 << index)) {
		sess->send_notify("领取过");
		return;
	}

	player->att(Pl4s_Power,
			player->att(Pl4s_Power) + g_game_cfg->DailyPowerNum);
	player->att(Pl4s_DailyPowerMask, mask | (1 << index));
}

void handle_cunlockwatch(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	int watch_num = player->att(Pl4s_WatchNum);
	if (watch_num >= PlayerCombatWSlotNum && watch_num < 1) {
		sess->send_notify("不在范围");
		return;
	}

	int32_t consumeid = g_game_cfg->UnlockWatchConsume[watch_num - 1];
	if (!player->can_consume(consumeid)) {
		sess->send_notify("消耗不足");
		return;
	}

	player->consume(consumeid);
	player->att(Pl4s_WatchNum, watch_num + 1);
}

void handle_cfriendgift(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	const auto &gift = msg->friendgift();
	cs::EFriendGiftType type = gift.type();
	int64_t friendid = gift.friend_();

	if (!EFriendGiftType_IsValid(type) || friendid < 0) {
		sess->send_notify("无效参数");
		return;
	}

	EPlAtt8Save slot = EPlAtt8Save(Pl8s_GiftFromMe1 + type - 1);
	if (player->att(slot) >= 0) {
		sess->send_notify("已经送了");
		return;
	}

	int32_t consumeid = g_game_cfg->FriendGiftConsume[type - 1];
	if (consumeid < 0) {
		sess->send_notify("无效消耗id");
		return;
	}

	if (!player->can_consume(consumeid)) {
		sess->send_notify("不能消耗");
		return;
	}

	if (player->id == friendid) {
		sess->send_notify("can not gift self");
		return;
	}

	// 0正在进行中
	player->att(slot, 0);

	g_playermgr->player_load(friendid,
			[consumeid, slot, friendid, type, player](sp_player_t toplayer) {
				if (!toplayer) {
					if (player)
					player->sess->send_notify("无此玩家");
					player->att(slot, -1);
					return;
				}

				EPlAtt8Save toslot = EPlAtt8Save(Pl8s_GiftToMe1 + type - 1);
				if (toplayer->att(toslot) >= 0) {
//					if (player->sess)
//						player->sess->send_notify("friend gifted");
					player->att(slot, -1);
					return;
				}

				if (!player->can_consume(consumeid)) {
					if (player->sess)
					player->sess->send_notify("不能消耗");
					player->att(slot, -1);
					return;
				}

				player->consume(consumeid);

				player->att(slot, friendid);
				toplayer->att(toslot, player->id);

				player->add_giftlog(player->id, toplayer->att(Plss_Name), player->att(Plss_Name), type);
				toplayer->add_giftlog(player->id, toplayer->att(Plss_Name), player->att(Plss_Name), type);

				int32_t fromrewardid = g_game_cfg->FriendGiftFromGain[type - 1];
				int32_t torewardid = g_game_cfg->FriendGiftToGain[type - 1];

				if (fromrewardid >= 0)
				g_mail->send_system(
						player->id,
						EMT_FriGiftRewardSelf,
						toplayer->att(Plss_Name),
						fromrewardid,
						1.0f,
						[player, toplayer, fromrewardid](bool ok) {
							if (!ok) {
								UTIL_LOG(E, "friend gift from:%lld to:%lld reward:%d save err", player->id, toplayer->id, fromrewardid);
							}
						});
				if (torewardid >= 0)
				g_mail->send_system(
						toplayer->id,
						EMT_FriGiftReward,
						player->att(Plss_Name),
						torewardid,
						1.0f,
						[player, toplayer, torewardid](bool ok) {
							if (!ok) {
								UTIL_LOG(E, "friend gift from:%lld to:%lld reward:%d save err", player->id, toplayer->id, torewardid);
							}
						});

				if (!toplayer->sess)
				toplayer->add_save_queue(true);
				if (!player->sess)
				player->add_save_queue(true);
			});
}

void handle_ctrain(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	const auto &train = msg->train();
	switch (train.op()) {
	case 0:	// PUT INTO ROOM
	{
		int roomnum = g_game_cfg->RoomNum[player->att(Pl4s_VipLevel)];
		if (train.index() < 0 || train.index() >= roomnum) {
			sess->send_notify("invalid room index");
			return;
		}
		if (player->_train[train.index()].finish >= 0) {
			sess->send_notify("room busy");
			return;
		}

		for (int i = 0; i < MaxTrainRoomNum; ++i) {
			if (train.petid() == player->_train[i].petid) {
				sess->send_notify("pet busy");
				return;
			}
		}

		auto pet = player->find_pet(train.petid());
		if (NULL == pet) {
			sess->send_notify("pet not exist");
			return;
		}

		player->_train[train.index()].type = train.type();
		player->_train[train.index()].petlvl = pet->att(Pe4s_Level);
		player->_train[train.index()].finish = g_clock->tt_now
				+ g_game_cfg->TrainMin * 60;
		player->_train[train.index()].petid = pet->serial;

		player->_train_mods.insert(train.index());
	}
		break;
	case 1: // GET GAIN
	{
		if (player->_train[train.index()].finish < 0) {
			sess->send_notify("room busy");
			return;
		}

		if (g_clock->tt_now < player->_train[train.index()].finish) {
			sess->send_notify("not finish");
			return;
		}

		const auto *proto = ARProto(TrainReward,
				player->_train[train.index()].petlvl);
		if (NULL == proto) {
			player->_train[train.index()].petid = -1;
			player->_train[train.index()].petlvl = -1;
			player->_train[train.index()].finish = -1;
			player->_train_mods.insert(train.index());
			sess->send_notify("invalid train reward proto");
			return;
		}

		switch (player->_train[train.index()].type) {
		case 0: {
			auto pet = player->find_pet(player->_train[train.index()].petid);
			if (NULL == pet) {
				player->_train[train.index()].petid = -1;
				player->_train[train.index()].petlvl = -1;
				player->_train[train.index()].finish = -1;
				player->_train_mods.insert(train.index());
				sess->send_notify("pet not exist");
				return;
			}

			int roomlvl = player->_train[train.index()].lvl;
			if (roomlvl >= 0 && roomlvl < 5)
				pet->add_exp(proto->exp * g_game_cfg->RoomLvlExpRate[roomlvl]);
		}
			break;
		case 1: {
			int roomlvl = player->_train[train.index()].lvl;
			if (roomlvl >= 0 && roomlvl < 5)
				player->att(Pl8s_GameMoney,
						player->att(Pl8s_GameMoney)
								+ proto->gamemoney
										* g_game_cfg->RoomLvlExpRate[roomlvl]);
		}
			break;
		default:
			player->_train[train.index()].petid = -1;
			player->_train[train.index()].petlvl = -1;
			player->_train[train.index()].finish = -1;
			player->_train_mods.insert(train.index());
			sess->send_notify("invalid room type");
			return;
			break;
		}

		player->_train[train.index()].petid = -1;
		player->_train[train.index()].petlvl = -1;
		player->_train[train.index()].finish = -1;

		player->_train_mods.insert(train.index());
	}
		break;
	case 2: // LEVEL UP ROOM
	{
		if (player->_train[train.index()].lvl >= 4) {
			sess->send_notify("room full level");
			return;
		}

		if (player->_train[train.index()].finish >= 0) {
			sess->send_notify("room busy");
			return;
		}

		int32_t consumeid =
		g_game_cfg->RoomLvlupConsume[player->_train[train.index()].lvl];
		if (!player->can_consume(consumeid)) {
			sess->send_notify("consume not enough");
			return;
		}

		player->consume(consumeid);

		player->_train[train.index()].lvl++;

		player->_train_mods.insert(train.index());
	}
		break;
	default:
		break;
	}
}

void handle_cfloorreset(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	int resetnum = player->att(Pl4s_FloorResetNum);
	if (resetnum > 3 || resetnum < 0) {
		sess->send_notify("num not enough");
		return;
	}

	int32_t consumeid = g_game_cfg->FloorResetNumConsume[resetnum];
	if (consumeid >= 0) {
		if (!player->can_consume(consumeid)) {
			sess->send_notify("consume not enough");
			return;
		}
		player->consume(consumeid);
	}

	player->att(Pl4s_FloorResetNum, resetnum + 1);
	player->att(Pl4s_CurFloor, 0);
}

void handle_cfloorcombat(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	int32_t petid = msg->floorcombat().petid();
	int32_t dst_floor = msg->floorcombat().dst_floor();
	int curfloor = player->att(Pl4s_CurFloor);
	int maxfloor = player->att(Pl4s_MaxFloor);

	if (dst_floor > 0) {
		const auto* proto = ARProto(Floor, maxfloor);
		if (NULL == proto) {
			sess->send_notify("has no such floor");
			return;
		}

		if (!player->can_consume(proto->jump_consume)) {
			sess->send_notify("consume not enough");
			return;
		}
		player->consume(proto->jump_consume);

		CREATE_CS_SMSG(SFloorCombat, floorcombat);
		for (int i = curfloor + 1; i <= maxfloor; ++i) {
			const auto* proto = ARProto(Floor, i);
			if (NULL == proto)
				continue;

			const auto* lvlproto = MAProto(Level, proto->levelid);
			if (NULL == lvlproto)
				continue;

			player->reward(lvlproto->reward, floorcombat->add_reward(), 1.0f,
					petid);
		}
		sess->send_msg(___msg);
		player->att(Pl4s_CurFloor, proto->id);
	} else {
		auto pet = player->find_pet(petid);
		if (NULL == pet) {
			sess->send_notify("no pet");
			return;
		}

		const auto* proto = ARProto(Floor, curfloor + 1);
		if (NULL == proto) {
			sess->send_notify("invalid floor");
			return;
		}

		CREATE_CS_SMSG(SFloorCombat, floorcombat);
		g_combatcookie(player.get(), NULL, 0);
		bool res = g_combat(player.get(), pet, proto->levelid,
				floorcombat->mutable_combat());
		floorcombat->set_levelid(proto->levelid);
		sess->send_msg(___msg);
		if (res) {
			player->att(Pl4s_CurFloor, proto->id);
			player->att(Pl4s_MaxFloor, max(proto->id, maxfloor));
		}
	}
}

void handle_cfloorreward(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	if (player->bag_free_slot() < 1) {
		sess->send_notify("背包满了");
		return;
	}

	int dstfloor = msg->floorreward().dst_floor();
	int rewardlevel = player->att(Pl4s_FloorRewardLevel);
	int maxfloor = player->att(Pl4s_MaxFloor);

	if (dstfloor <= rewardlevel) {
		sess->send_notify("invalid cur reward level");
		return;
	}

	if (dstfloor > maxfloor) {
		sess->send_notify("invalid dstfloor");
		return;
	}

	const auto* proto = ARProto(Floor, dstfloor);
	if (NULL == proto) {
		sess->send_notify("invalid floor");
		return;
	}

	const auto* rewardproto = MAProto(Reward, proto->special_reward);
	if (NULL == proto) {
		sess->send_notify("invalid special reward proto");
		return;
	}

	player->reward(proto->special_reward);
	player->att(Pl4s_FloorRewardLevel, dstfloor);
}

void handle_frivalue(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	auto pet = player->find_pet(msg->frivalue().petid());
	if (!pet) {
		sess->send_notify("无此宠物");
		return;
	}

	if (player->att(Pl4s_FriFeedNum) >= g_game_cfg->FriValueAddTimePerDay) {
		sess->send_notify("次数已满");
		return;
	}

	int fristage = pet->att(Pe4s_FStage);
	const tagProtoPetFriendStage* proto = ARProto(PetFriendStage, fristage);
	if (!proto) {
		sess->send_notify("无效阶段");
		return;
	}

	if (!player->can_consume(proto->feed_consumeid)) {
		sess->send_notify("消耗不够");
		return;
	}

	int frival = pet->att(Pe4s_FriVal);
	if (frival >= proto->req_friendvalue) {
		sess->send_notify("友好度满了");
		return;
	}

	player->consume(proto->feed_consumeid);

	pet->att(Pe4s_FriVal,
			cut(frival + g_game_cfg->FriValueAddNum, 0,
					proto->req_friendvalue));
	player->att(Pl4s_FriFeedNum, player->att(Pl4s_FriFeedNum) + 1);
}

void handle_cfristageup(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	auto pet = player->find_pet(msg->fristageup().petid());
	if (!pet) {
		sess->send_notify("无此宠物");
		return;
	}

	int fristage = pet->att(Pe4s_FStage);
	const tagProtoPetFriendStage* proto = ARProto(PetFriendStage, fristage);
	if (!proto) {
		sess->send_notify("无效阶段");
		return;
	}

	const tagProtoPetFriendStage* nextproto = ARProto(PetFriendStage,
			fristage + 1);
	if (!proto) {
		sess->send_notify("无效阶段");
		return;
	}

	if (!player->can_consume(proto->next_consumeid)) {
		sess->send_notify("消耗不够");
		return;
	}

	int frival = pet->att(Pe4s_FriVal);
	if (frival < proto->req_friendvalue) {
		sess->send_notify("亲密度不够");
		return;
	}

	player->consume(proto->next_consumeid);
	pet->att(Pe4s_FriVal, 0);
	pet->att(Pe4s_FStage, nextproto->id);

	int idx = rand() % nextproto->g_randatts.size();
	pet->att(EPeAtt4Save(Pe4s_FSAtt1 + nextproto->id),
			nextproto->g_randatts[idx].att);
	pet->att(EPeAtt4Save(Pe4s_FSMod1 + nextproto->id),
			random(nextproto->g_randatts[idx].min,
					nextproto->g_randatts[idx].max));

	pet->calc_att();
}

void handle_cfristagewash(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (NULL == player)
		return;

	auto pet = player->find_pet(msg->fristagewash().petid());
	if (!pet) {
		sess->send_notify("无此宠物");
		return;
	}

	int fristage = pet->att(Pe4s_FStage);
	const tagProtoPetFriendStage* proto = ARProto(PetFriendStage, fristage);
	if (!proto) {
		sess->send_notify("无效阶段");
		return;
	}

	int32_t consumeid = -1;
	if (msg->fristagewash().money()) {
		consumeid = proto->wash_m_consumeid;
	} else {
		consumeid = proto->wash_g_consumeid;
	}
	if (!player->can_consume(consumeid)) {
		sess->send_notify("消耗不够");
		return;
	}
	player->consume(consumeid);

	const vector<tagProtoPetFriendStage::randatt_t>* randatts;
	for (int i = 0; i <= fristage; ++i) {
		proto = ARProto(PetFriendStage, i);
		if (msg->fristagewash().money()) {
			randatts = &proto->m_randatts;
		} else {
			randatts = &proto->g_randatts;
		}
		int idx = rand() % randatts->size();
		pet->att(EPeAtt4Save(Pe4s_FSAtt1 + i), randatts->at(idx).att);
		pet->att(EPeAtt4Save(Pe4s_FSMod1 + i),
				random(randatts->at(idx).min, randatts->at(idx).max));
	}
	pet->calc_att();
	player->att(Pl4s_WeeklyCnt7PetFriFeed,
			player->att(Pl4s_WeeklyCnt7PetFriFeed) + 1);
}

void handle_cspriteformlevelup(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	int form_level = player->att(Pl4s_FormLevel);
	const auto* proto = ARProto(SpriteFormLevel, form_level);
	if (!proto) {
		sess->send_notify("invalid form_level proto");
		return;
	}

	int anima = player->att(Pl4s_SpriteAnima);
	if (anima < proto->levelup_anima) {
		sess->send_notify("consume not enough");
		return;
	}

	player->att(Pl4s_SpriteAnima, anima - proto->levelup_anima);
	player->att(Pl4s_FormLevel, form_level + 1);
}

void handle_cspriteformrefresh(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	int form_level = player->att(Pl4s_FormLevel);
	const auto* proto = ARProto(SpriteFormLevel, form_level);
	if (!proto) {
		sess->send_notify("invalid form_level proto");
		return;
	}

	if (!player->can_consume(proto->refresh_consume)) {
		sess->send_notify("consume not enough");
		return;
	}

	player->consume(proto->refresh_consume);
	player->att(Pl4s_FormSeed, rand());
}

void handle_cpointrankopen(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	pointrank_t::member_t* mem = NULL;
	int group = -1;
	int groupsize = 0;
	int rank = pointrank_get_member(g_pr, player->id, mem, group);
	if (group >= 0) {
		groupsize = g_pr->groups[group].members.size();
	}

	CREATE_CS_SMSG(SPointRankOpen, pointrankopen);
	pointrankopen->set_finish(g_pr->finish);
	pointrankopen->set_rank(rank);
	pointrankopen->set_score(mem ? mem->points : 0);
	pointrankopen->set_group(group);
	pointrankopen->set_groupsize(groupsize);
	pointrankopen->set_cout_win(mem ? mem->win_con : 0);
	for (auto &itr : g_pr->groups)
		pointrankopen->add_last_winner(itr.last_win);
	for (auto &itr : g_pr->pet_family)
		pointrankopen->add_pet_family(itr);
	pointrankopen->set_rewarding(
	g_pr->rewarding_group >= 0 || g_pr->rewarding_index >= 0);

	sess->send_msg(___msg);
}

static void _check_stub(pointrank_t::member_t &mem, sp_player_t player) {
	if (!mem.stub || mem.stub->image_stamp < player->image_stamp) {
		Json::Value image;
		player->save(image);
		mem.stub.reset();
		mem.stub = make_shared<player_t>();
		mem.stub->init_image(image);
	}
}

void handle_cpointrankcombat(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	if (g_pr->rewarding_group >= 0 || g_pr->rewarding_index >= 0) {
		sess->send_notify("rewarding");
		return;
	}

	int combattimes = player->att(Pl4s_PointRankCombatTimes);
	if (combattimes <= 0) {
		sess->send_notify("no combattimes remain");
		return;
	}

	int32_t cdfinish = player->att(Pl4s_PointRankCooldown);
	if (g_clock->tt_now < cdfinish) {
		sess->send_notify("in cd");
		return;
	}

	pointrank_t::member_t* mem = NULL;
	int ngroup = -1;
	int idx = pointrank_get_member(g_pr, player->id, mem, ngroup);

	if (!pointrank_checkpets(g_pr, player)) {
		sess->send_notify("family restrict");
		return;
	}

	const auto* proto = ARProto(Medal, player->att(Pl4s_Medal));
	if (!proto) {
		sess->send_notify("invalid medal");
		return;
	}

	if (ngroup < 0 || idx < 0) {
		while (g_pr->groups.size() <= proto->group)
			g_pr->groups.push_back(pointrank_t::group_t());

		if (pointrank_get_member_num(g_pr) > 5000) {
			sess->send_notify("最大人数限制");
			return;
		}

		auto &group = g_pr->groups[proto->group];

		pointrank_t::member_t member;
		member.id = player->id;
		member.points = 0;
		member.win_con = 0;
		group.members.push_back(member);

		ngroup = proto->group;
		idx = group.members.size() - 1;
		mem = &group.members.back();

		_check_stub(*mem, player);
	}

	auto &group = g_pr->groups[ngroup];

	int32_t other_idx = cut<int32_t>(idx + rand() % 6 - 3, 0,
			group.members.size() - 1);
	assert(other_idx >= 0 && other_idx < group.members.size());
	if (other_idx == idx) {
		if (group.members.size() == 1) {
			other_idx = idx;
		} else if (idx == 0) {
			other_idx = idx + 1;
		} else {
			other_idx = idx - 1;
		}
	}

	assert(idx >= 0 && other_idx >= 0);

	_check_stub(group.members[idx], player);

	sp_player_t unit1 = group.members[idx].stub;
	sp_player_t unit2 = group.members[other_idx].stub;
	if (idx == other_idx) {
		static sp_player_t nullplayer;
		if (!nullplayer) {
			nullplayer = make_shared<player_t>();
			nullplayer->init(-1, "air", EPS_Male, "air");
		}
		nullplayer->id = player->id;
		nullplayer->att(Plss_Name, "空气");

		unit2 = nullplayer;
	}

	CREATE_CS_SMSG(SPointRankCombat, pointrankcombat);

	g_combatcookie(unit1.get(), unit2.get(), 0);
	bool win = g_combat(unit1.get(), unit2.get(),
			pointrankcombat->mutable_combat());
	if (win) {
		player->att(Pl8s_Honor,
				player->att(Pl8s_Honor) + g_game_cfg->PointrankHonor[0]);
		mem->points += (mem->win_con - 1) * g_game_cfg->PointrankContinueWinPoints
				+ g_game_cfg->PointrankPoints[0];
		++mem->win_con;
		if (mem->win_con > g_game_cfg->PointrankMaxContinueWin)
			mem->win_con = g_game_cfg->PointrankMaxContinueWin;
	} else {
		player->att(Pl8s_Honor,
				player->att(Pl8s_Honor) + g_game_cfg->PointrankHonor[1]);
		mem->points += g_game_cfg->PointrankPoints[1];
		mem->win_con = 0;
		player->att(Pl4s_PointRankCooldown,
		g_clock->tt_now + g_game_cfg->PointrankLoseCDSec);
	}

	pointrankcombat->set_score(mem->points);
	pointrankcombat->set_rank(idx);
	pointrankcombat->set_groupsize(group.members.size());
	pointrankcombat->set_cout_win(mem->win_con);

	sess->send_msg(___msg);

	player->att(Pl4s_PointRankCombatTimes, combattimes - 1);
	++group.update_count;
	++g_pr->change_count;
}

void handle_ccardreward(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	int total = player->pethas.size() + player->levelhas.get_1num()
			+ player->skillhas.size();
	int rewarded = player->att(Pl4s_CardRewarded);

	const auto* proto = ARProto(CardReward, rewarded+1);
	if (!proto) {
		sess->send_notify("无奖励可领");
		return;
	}

	if (total < proto->num) {
		sess->send_notify("数量不够");
		return;
	}

	player->att(Pl4s_CardRewarded, rewarded + 1);
	player->reward(proto->reward);
}

void handle_cseconddayreward(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;
	tm tmcreate = *localtime(&player->create);
	tm tmnow = *localtime(&g_clock->tt_now);

	tmcreate.tm_mday++;
	mktime(&tmcreate);

	if (tmnow.tm_year != tmcreate.tm_year &&
		tmnow.tm_mon != tmcreate.tm_mon &&
		tmnow.tm_mday != tmcreate.tm_mday) {
		sess->send_notify("过期了");
		return;
	}

	int mask = player->att(Pl4s_DailyPowerMask);
	if (mask & (1 << 2)) {
		sess->send_notify("领取过");
		return;
	}
	player->att(Pl4s_DailyPowerMask, mask | (1 << 2));
	player->reward(g_game_cfg->SecondDayReward);
}

void handle_cisinvitedby	 	(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	if (player->_inviter >= 0) {
		player->sess->send_notify("inviter already setted");
		return;
	}

	int64_t inviterid = msg->isinvitedby().inviterid() + co::MaxAccountNum * g_cfg->ServerId;
	bool ok = g_playermgr->player_load(
			inviterid,
			[player](sp_player_t inviter){
				if (player->sess == NULL)
					return;

				if (!inviter) {
					player->sess->send_notify("no such inviter");
					return;
				}

				if (player->_inviter >= 0) {
					player->sess->send_notify("inviter already setted");
					return;
				}

				if (!inviter) {
					player->sess->send_notify("inviter invalid");
					return;
				}

				if (inviter->_invited.count(player->id) > 0) {
					player->sess->send_notify("already invited");
					return;
				}

				if (inviter->_invited.size() >= 30) {
					player->sess->send_notify("max invited");
					return;
				}

				inviter->_invited.insert(player->id);
				inviter->_invited_added.insert(player->id);

				player->_inviter = inviter->id;
				player->_inviter_chged = true;
			});

	if (!ok)
		sess->send_notify("db err");
}

void handle_cgaininvitereward	(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	int id = msg->gaininvitereward().id();

	auto proto = ARProto(InviteReward, id);
	if (!proto) {
		sess->send_notify("invalid proto");
		return;
	}

	int64_t rewardmask = player->att(Pl8s_InviteRewardMask);
	if (rewardmask & 1<<id) {
		sess->send_notify("rewarded");
		return;
	}

	int32_t medal_count[300] = {0};
//	map<int64_t, int> medal_count;
	for (auto &itr : player->_invited) {
		auto info = g_playermgr->infos.get(itr);
		if (!info)
			continue;

		for (int i=0; i<=info->medal; ++i)
			medal_count[i]++;
	}

	if (medal_count[proto->medal] < proto->num) {
		sess->send_notify("medal num not enough");
		return;
	}

	player->reward(proto->reward);

	rewardmask |= 1<<id;

	player->att(Pl8s_InviteRewardMask, rewardmask);
}

void handle_cinvitedreward		(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	if (player->att(Pl4s_InvitedRewarded) != 0) {
		sess->send_notify("领取过");
		return;
	}

	player->reward(g_game_cfg->InvitedReward);

	player->att(Pl4s_InvitedRewarded, 1);
}

void handle_cguildlist          (client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	const auto &cguildlist = msg->guildlist();
	int start = cguildlist.start();
	int num = cguildlist.num();
	int len = g_guildmgr->rank.size();

	CREATE_CS_SMSG(SGuildList, guildlist);
	guildlist->set_start(start);
	for (int i=0; i<num; ++i) {
		int idx = i+start;
		if (idx >= len)
			break;

		auto guild = g_guildmgr->rank[idx];
		assert(guild);
		guild->fill_info(guildlist->add_infos());
	}
	sess->send_msg(___msg);
}
void handle_cguilddetail        (client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	int32_t guildid = player->att(Pl4n_GuildId);
	if (guildid <= 0) {
		sess->send_notify("you have no guild");
		return;
	}

	auto guild = g_guildmgr->guilds.get(guildid);
	if (!guild) {
		sess->send_notify("invalid guild");
		return;
	}

	CREATE_CS_SMSG(SGuildDetail, guilddetail);
	guild->fill_info(guilddetail->mutable_info());
	for (auto itr : guild->members)
		itr.second->fill_info(guilddetail->add_members());
	sess->send_msg(___msg);

}
void handle_cguildcreate        (client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	if (player->att(Pl4n_GuildId) >= 0) {
		sess->send_notify("you has guild");
		return;
	}

	if (!player->can_consume(g_game_cfg->GuildCreateConsume)) {
		sess->send_notify("consume not enough");
		return;
	}

	const auto &cguildcreate = msg->guildcreate();

	if (g_guildmgr->name2id.count(cguildcreate.name()) > 0) {
		sess->send_notify("name already exist");
		return;
	}

	player->consume(g_game_cfg->GuildCreateConsume);

	guild_t* guild = new guild_t;
	guild->id = g_guildmgr->guildid++;
	guild->name = cguildcreate.name();
	guild->intro = cguildcreate.intro();
	guild->contrib = 0;
	guild->rank = -1;

	g_guildmgr->name2id[guild->name] = guild->id;
	g_guildmgr->guilds[guild->id] = guild;

	guild->add_member(player->id, EGP_Head);
	g_guildmgr->on_player_guildid(player->id, guild->id);

	g_guildmgr->add_rank(guild);

	player->att(Pl4n_GuildId, guild->id);
	player->att(Pl4n_GuildPos, EGP_Head);
}

void handle_cguildjoin          (client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	const auto &cguildjoin = msg->guildjoin();
	if (cguildjoin.has_guildid() && !cguildjoin.has_playerid()) {
		if (player->att(Pl4n_GuildId) >= 0) {
			sess->send_notify("you has guild");
			return;
		}
		auto guild = g_guildmgr->guilds.get(cguildjoin.guildid());
		if (!guild) {
			sess->send_notify("no such guild");
			return;
		}

		if (guild->appliers.count(player->id) > 0) {
			sess->send_notify("already applied");
			return;
		}

		if (guild->members.size() >= g_game_cfg->GuildMemberNum) {
			sess->send_notify("member num exceed");
			return;
		}

		guild->appliers.insert(player->id);
	} else if (!cguildjoin.has_guildid() && cguildjoin.has_playerid()) {
		int32_t guildid = player->att(Pl4n_GuildId);
		if (guildid < 0) {
			sess->send_notify("you has not guild");
			return;
		}

		auto guild = g_guildmgr->guilds.get(guildid);
		if (!guild) {
			sess->send_notify("invalid guild");
			return;
		}

		if (cguildjoin.playerid() < 0) {
			sess->send_notify("invalid playerid");
			return;
		}
		auto playerinfo = g_playermgr->infos.get(cguildjoin.playerid());
		if (!playerinfo) {
			sess->send_notify("invalid playerinfo");
			return;
		}

		if (guild->appliers.count(playerinfo->playerid) == 0) {
			sess->send_notify("no applied");
			return;
		}

		guild->appliers.erase(playerinfo->playerid);

		if (cguildjoin.has_ok() && cguildjoin.ok()) {
			if (g_guildmgr->player2guild.count(playerinfo->playerid) > 0) {
				sess->send_notify("has guild");
				return;
			}

			if (guild->members.size() >= g_game_cfg->GuildMemberNum) {
				sess->send_notify("member num exceed");
				return;
			}

			guild->add_member(player->id, EGP_Normal);
			g_guildmgr->on_player_guildid(player->id, guild->id);

			auto toadd = g_playermgr->get_player(playerinfo->playerid);
			if (toadd) {
				toadd->att(Pl4n_GuildId, guild->id);
				toadd->att(Pl4n_GuildPos, EGP_Normal);
			}
		}
	}
}
void handle_cguildquit          (client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	int32_t guildid = player->att(Pl4n_GuildId);
	if (guildid < 0) {
		sess->send_notify("not in guild");
		return;
	}

	auto guild = g_guildmgr->guilds.get(guildid);
	if (!guild) {
		sess->send_notify("invalid guild");
		return;
	}

	auto member = guild->get_member(player->id);
	if (!member) {
		sess->send_notify("no such player");
		return;
	}

	if (msg->guildquit().has_thatplayer()) {
		int64_t thatplayerid = msg->guildquit().thatplayer();
		if (thatplayerid == player->id) {
			sess->send_notify("couldn't be your self");
			return;
		}

		auto thatmember = guild->get_member(thatplayerid);
		if (!thatmember) {
			sess->send_notify("that player not member");
			return;
		}

		if (thatmember->pos != EGP_Normal) {
			sess->send_notify("that player is leader");
			return;
		}

		if (member->pos != EGP_Head) {
			sess->send_notify("you are not leader");
			return;
		}

		guild->del_member(player->id);
		g_guildmgr->on_player_guildid(player->id, -1);

		int32_t contrib = member->contrib;
		g_playermgr->player_load(thatplayerid, [contrib](sp_player_t thatplayer){
			if (thatplayer) {
				thatplayer->att(Pl4n_GuildId, -1);
				thatplayer->att(Pl4n_GuildPos, 0);
			}
		});
	} else {
		if (member->pos == EGP_Head) {
			sess->send_notify("you are leader");
			return;
		}

		guild->del_member(player->id);
		g_guildmgr->on_player_guildid(player->id, -1);

		player->att(Pl4n_GuildId, -1);
		player->att(Pl4n_GuildPos, 0);
	}

}
void handle_cguildchgpos        (client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	int32_t guildid = player->att(Pl4n_GuildId);
	if (guildid < 0) {
		sess->send_notify("you are not in guild");
		return;
	}

	if (player->att(Pl4n_GuildPos) != EGP_Head) {
		sess->send_notify("you are not head");
		return;
	}

	const auto &cguildchgpos = msg->guildchgpos();

	int64_t thatplayerid = cguildchgpos.playerid();
	auto itr = g_guildmgr->player2guild.find(thatplayerid);
	if (itr == g_guildmgr->player2guild.end()) {
		sess->send_notify("he is not in guild");
		return;
	}

	if (itr->second != guildid) {
		sess->send_notify("he is not in your guild");
		return;
	}

	auto guild = g_guildmgr->guilds.get(guildid);
	if (!guild) {
		sess->send_notify("no such guild");
		return;
	}

	auto leadermember = guild->get_member(player->id);
	auto thatmember = guild->get_member(thatplayerid);
	assert(leadermember && thatmember);
	thatmember->pos = EGP_Head;
	leadermember->pos = EGP_Normal;
	guild->leader = thatmember->playerid;

	player->att(Pl4n_GuildPos, EGP_Normal);
	auto thatplayer = g_playermgr->get_player(thatplayerid);
	if (thatplayer) {
		thatplayer->att(Pl4n_GuildPos, EGP_Head);
	}
}

void handle_cguildchgintro      (client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	int32_t guildid = player->att(Pl4n_GuildId);
	if (guildid < 0) {
		sess->send_notify("you are not in guild");
		return;
	}

	if (player->att(Pl4n_GuildPos) != EGP_Head) {
		sess->send_notify("you are not head");
		return;
	}

	const auto &cguildchgintro = msg->guildchgintro();
	auto guild = g_guildmgr->guilds.get(guildid);
	if (!guild) {
		sess->send_notify("no such guild");
		return;
	}

	guild->intro = cguildchgintro.intro();
}

void handle_cguildapplications	(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	if (player->att(Pl4n_GuildId) < 0) {
		sess->send_notify("has not guild");
		return;
	}

	if (player->att(Pl4n_GuildPos) != EGP_Head) {
		sess->send_notify("has not permission");
		return;
	}

	auto guild = g_guildmgr->guilds.get(player->att(Pl4n_GuildId));
	if (!guild) {
		sess->send_notify("invalid guild");
		return;
	}

	CREATE_CS_SMSG(SGuildApplications, guildapplications);
	for (auto playerid : guild->appliers) {
		auto info = g_playermgr->infos.get(playerid);
		if (info) {
			auto application = guildapplications->add_application();
			application->set_name(info->name);
			application->set_id(info->playerid);
			application->set_combatforce(info->total_combat_force);
		}
	}
	sess->send_msg(___msg);
}

void handle_cpetmedal			(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	if (g_clock->tt_now > g_svr.server_begin + g_game_cfg->PetStarGainLast * 60 * 60 * 24) {
		sess->send_notify("expire");
		return;
	}

	if (player->att(Pl4s_PetMedal) >= 0) {
		sess->send_notify("already gained");
		return;
	}
	const auto &cpetrecord = msg->petrecord();

	auto pet = player->find_pet(cpetrecord.petserial());
	if (!pet) {
		sess->send_notify("no such pet");
		return;
	}

	if (pet->att(Pe4s_Star) < g_game_cfg->PetMedalStar) {
		sess->send_notify("no enough star");
		return;
	}

	Json::Value petmedal;
	petmedal["objtype"] = "petmedal";
	petmedal["playerid"] = Json::Int64(player->id);
	petmedal["petserial"] = pet->serial;
	petmedal["protoid"] = pet->proto->id;
	petmedal["account"] = player->account;
	petmedal["contract"] = cpetrecord.contract();
	petmedal["time"] = Json::Int64(g_clock->tt_now);
	string key = fmt_cstr("petmedal:%d:%d", g_cfg->ServerId, player->id % co::MaxAccountNum);
	int32_t petprotoid = pet->proto->id;
	g_dbf->set(key.c_str(), petmedal, [player, petprotoid](db_error_t err){
		if (err == DBE_Success) {
			player->att(Pl4s_PetMedal, petprotoid);
		}
	});
}

void handle_cpetcert			(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	if (g_clock->tt_now > g_svr.server_begin + g_game_cfg->PetStarGainLast * 60 * 60 * 24) {
		sess->send_notify("expire");
		return;
	}

	if (player->att(Pl4s_PetCert) >= 0) {
		sess->send_notify("already gained");
		return;
	}
	const auto &cpetrecord = msg->petrecord();

	auto pet = player->find_pet(cpetrecord.petserial());
	if (!pet) {
		sess->send_notify("no such pet");
		return;
	}

	if (pet->att(Pe4s_Star) < g_game_cfg->PetCertStar) {
		sess->send_notify("no enough star");
		return;
	}

	Json::Value petcert;
	petcert["objtype"] = "petcert";
	petcert["playerid"] = Json::Int64(player->id);
	petcert["petserial"] = pet->serial;
	petcert["protoid"] = pet->proto->id;
	petcert["account"] = player->account;
	petcert["contract"] = cpetrecord.contract();
	petcert["time"] = Json::Int64(g_clock->tt_now);
	string key = fmt_cstr("petcert:%d:%d", g_cfg->ServerId, player->id % co::MaxAccountNum);
	int32_t petprotoid = pet->proto->id;
	g_dbf->set(key.c_str(), petcert, [player, petprotoid](db_error_t err){
		if (err == DBE_Success) {
			player->att(Pl4s_PetCert, petprotoid);
		}
	});
}

void handle_cluckystarseed		(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	player->att(Pl4n_LuckStarSeed, g_svr.LuckyStarSeed);
}

void handle_ccombatrewardgain	(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	int index = msg->combatrewardgain().index();
	if (index < 0 || index >= 3) {
		sess->send_notify("err index");
		return;
	}

	int rewardid = player->combatrewards[index];
	if (rewardid < 0) {
		sess->send_notify("invalid reward");
		return;
	}

	player->combatrewards[index] = -1;
	player->reward(rewardid);
}

void handle_csharereward		(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	int32_t type = msg->sharereward().type();
	if (type > g_game_cfg->ShareRewardNum || type >= 32) {
		sess->send_notify("invalid type");
		return;
	}

	int32_t mask = player->att(Pl4s_ShareRewardMask);
	if (mask & 1<<type) {
		sess->send_notify("already have");
		return;
	}

	player->reward(g_game_cfg->ShareReward);

	mask |= 1<<type;
	player->att(Pl4s_ShareRewardMask, mask);
}
